by Abu Zubayr

Showing posts with label Golang. Show all posts
Showing posts with label Golang. Show all posts

Redis dan Implementasinya di Golang

Bismillah...

Redis merupakan sebuah sistem basis data berbasis key-value store. Jika anda belum mengetahui apa itu key-value store maka akan saya jelaskan sedikit mengenai hal tersebut. Key-value adalah non-relational database yang menyimpan data dengan metode penyimpanan key-value. Jadi data yang disimpan adalah berupa key sebagai unique identifier terhadap sebuah value. Hal ini tentu berbeda dengan database pada umumnya yang kita jumpai seperti di MySQL dimana data yang disimpan berupa tabel-tabel.

Untuk lokasi penyimpanan data, redis juga berbeda dengan database lain pada umumnya. Dimana penyimpanan data redis dilakukan di memory bukan di hard drive. Kemudian dari segi penggunaanya, redis digunakan untuk menyimpan data yang sifatnya non permanen, oleh sebab itu jika sebuah server mati maka seluruh data tadi akan ikut hilang.

Salah satu contoh implementasi penggunaan redis adalah untuk penyimpanan data session di sebuah website. Dimana biasanya secara default data session tersebut tersimpan dalam sebuah flat file. Jika dibandingkan dari segi kecepatan akses data, maka redis jauh lebih cepat jika dibandingkan dengan session yang disimpan di flat file biasa. Hal ini dikarenakan redis mengakses datanya langsung ke memory sebuah pc/komputer yang tentu akan jauh lebih cepat.

Hingga kini redis bisa digunakan dihampir seluruh bahasa pemgrograman populer seperti Java, PHP, Node JS, Golang, Python, C, dan bahasa lainnya yang bisa anda lihat di https://redis.io/clients. Pada tutorial ini saya akan coba mengimplementasikan redis ini dibahasa pemrograman Golang. Di go sendiri terdapat banyak module yang bisa anda gunakan, akan tetapi pada tutorial ini saya akan menggunakan module go-redis/redis. Oke, tanpa berlama-lama langsung saja berikut adalah langkah-langkah penggunaan redis di golang :
  1. Buat sebuah project golang misalnya dengan nama project “redis-golang”
  2. Buka terminal dan lakukan Inisialisasi project baru tersebut dengan perintah go mod init redis-golang
  3. Tambahkan module redis digolang dengan perintah go get github.com/go-redis/redis/v8
  4. Buat sebuah file misalnya dengan nama server.js
  5. Di file server.js yang pertama yang perlu dilakukan adalah membuat koneksi. Caranya adalah seperti dibawah ini.
  6. con := redis.NewClient(&redis.Options{
      Addr: "localhost:6379",
      Password: "",
    })
    Pada source code diatas terlihat kita membuat sebuah koneksi ke redis dengan host/address dari redis sendiri di “localhost” yang berjalan di port “6379”. Sesuaikan dengan settingan redis di pc/laptop anda, kemudian jika menggunakan password, maka masukkan sekaligus di parameter Password tersebut.
  7. Setelah selesai membuat koneksi diatas, selanjutnya adalah mencoba membuat perintah yang biasa digunakan di redis seperti set, get dan delete. Sebelum memasukkan perintah tersebut, buat sebuah context terlebih dahulu seperti dibawah ini :
  8. ctx := context.Background()
    • Set
    • Perintah `set` adalah perintah yang digunakan untuk menambah atau melakukan perubahan data berdasarkan key tertentu. Untuk memasukkan perintah tersebut caranya adalah sebagai berikut :
      err := con.Set(ctx, "token", "101", 0).Err()
      if err != nil {
         panic(err)
      }
      Pada code diatas, method Set menerima parameter berupa (context, key, value dan exipiration). Berdasarkan data yang dimasukkan diatas, maka data yang dimasukkan memiliki key dengan nama token dan berisi value 101. Kemudian untuk expiration-nya kita set dengan 0 artinya data tersebut tidak memiliki masa expired alias akan terus ada di memory redis selama server tidak mati atau data tersebut tidak dihapus.
    • Get
    • Perintah `get` sendiri digunakan untuk melakukan pengambilan data yang sebelumnya sudah dimasukkan ke redis. Untuk melakukan perintah tersebut di golang caranya adalah sebagai berikut :
      val, err := con.Get(ctx, "token").Result()
      if err != nil {
        panic(err)
      }
      
      fmt.Println(val)
      Pada code diatas, method Get memiliki parameter (context dan key) dimana parameter key adalah merupakan key dari data yang sebelumnya sudah kita masukkan kedalam redis. Ketika data dengan key tersebut ada, maka variable val akan terisi data dari key tersebut. Namun jika data dengan key tadi tidak ada maka variable err akan berisi pesan error bahwa data dengan key tersebut tidak ada di server redis.
    • Delete
    • Selanjutnya di redis juga terdapat perintah `delete` dimana perintah ini digunakan untuk menghapus data yang ada di server redis. Untuk melakukan perintah ini di golang caranya adalah sebagai berikut :
      err = con.Del(ctx, "token").Err()
      if err != nil {
         panic(err)
      }
      Sama seperti method get tadi, method `delete` ini juga memiliki parameter berupa (context dan key). Jika data yang di hapus tersebut ada di database, maka data akan terhapus melalui method tersebut, akan tetapi jika data tersebut tidak ada maka kita tampung error tersebut kedalam variable err.
    • Increment & Decrement
    • Sebagai tambahan, redis juga bisa melakukan proses increment dan decrement yang umumnya sering kita jumpai di bahasa pemrograman. Caranya adalah sebagai berikut :
      Increment
      err = con.Incr(ctx, "angka").Err()
      if err != nil {
         panic(err)
      }
      
      Code diatas, method increment menerima parameter berupa (context dan key), namun disini perlu diperhatikan bahwa data yang akan dilakukan increment harus berupa angka. Method `Incr` ini juga melakukan increment dengan step 1, untuk melakukan increment dengan step yang sesuai dengan keinginan anda, maka bisa menggunakan method `IncrBy` seperti dibawah ini :
      err = con.IncrBy(ctx, "angka", 10).Err()
      if err != nil {
         panic(err)
      }
      Terlihat di code diatas, method `IncrBy` menerima parameter berupa (context, key, dan step increment-nya). Pada contoh diatas, saya memasukkan step-nya 10. Jadi data dengan key angka akan bertambah sebanyak 10.
      Decrement
      err = con.Decr(ctx, "angka").Err()
      if err != nil {
        panic(err)
      }
      Method `Decr` diatas menerima parameter (context dan key) dengan menggunakan method ini, proses decrement akan dilakukan dengan step 1 setiap method tersebut dipanggil atau digunakan. Untuk melakukan decrement dengan nilai custom step, caranya adalah seperti dibawah ini dengan menggunakan method `DecrBy` :
      err = con.DecrBy(ctx, "angka", 4).Err()
      if err != nil {
         panic(err)
      }
      Pada method diatas memiliki parameter (context, key dan step decrement-nya). Pada method diatas decrement dilakukan dengan 4 step.
Untuk source code lengkapnya seperti dibawah ini :
package main

import (
	"context"
	"fmt"

	"github.com/go-redis/redis/v8"
)

func main() {
	con := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
	})

	ctx := context.Background()
	err := con.Set(ctx, "token", "101", 0).Err()
	if err != nil {
		panic(err)
	}

	val, err := con.Get(ctx, "token").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println(val)

	err = con.Del(ctx, "token").Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "token").Result()
	if err != nil {
		fmt.Println("Data kosong")
	}

	fmt.Println("Data dengan key token setelah dihapus ", val)

	fmt.Println("\nTes set angka/numeric di redis")
	err = con.Set(ctx, "angka", 0, 0).Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "angka").Result()
	if err != nil {
		fmt.Println("Data dengan key angka kosong")
	} else {
		fmt.Println("Angka sebelum increment ", val)
	}

	err = con.Incr(ctx, "angka").Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "angka").Result()
	if err != nil {
		fmt.Println("Data dengan key angka kosong")
	} else {
		fmt.Println("Angka setelah increment ", val)
	}

	err = con.IncrBy(ctx, "angka", 10).Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "angka").Result()
	if err != nil {
		fmt.Println("Data dengan key angka kosong")
	} else {
		fmt.Println("Angka setelah increment 10 = ", val)
	}

	err = con.Decr(ctx, "angka").Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "angka").Result()
	if err != nil {
		fmt.Println("Data dengan key angka kosong")
	} else {
		fmt.Println("Angka setelah decrement = ", val)
	}

	err = con.DecrBy(ctx, "angka", 4).Err()
	if err != nil {
		panic(err)
	}

	val, err = con.Get(ctx, "angka").Result()
	if err != nil {
		fmt.Println("Data dengan key angka kosong")
	} else {
		fmt.Println("Angka setelah decrement 4 = ", val)
	}
}

Demikian postingan saya mengenai Redis dan Implementasinya di Golang, Jika ada pertanyaan silahkan ditanyakan dikolom komentar dibawah. Source code dari project ini sendiri bisa diakses melalui github di https://github.com/sardiabuzubayr/redis-golang

Demikian, semoga bermanfaat wassalamualaikum warahmatullah wabarakaatuh.
Share:

Belajar GraphQL, Serta Contoh Implementasinya di Golang

https://celiksoftware.blogspot.com/2022/01/belajar-graphql-serta-contoh.html


Bismillah

Saat membuat sebuah API, terkadang tidak hanya satu jenis platform yang meng-consume api tersebut. Di era sekarang ini, saat men-develop sebuah aplikasi berbasis website, maka juga akan diiringi dengan aplikasi mobile-nya baik Android maupun IOS. Jika jenis dan jumlah data yang dikirim oleh backend disamakan antara platform website dan mobile tadi, maka akan menjadi kurang efektif karena belum tentu data yang dibutuhkan oleh aplikasi website sama dengan data yang dibutuhkan oleh mobile, baik dari jenis dan jumlah datanya. Mungkin saja di website membutuhkan data dengan 5 atau seluruh field akan ditampilkan, tetapi di mobile mungkin hanya beberapa field saja dan jumlah barisnya pun terbatas.

Untuk menangani jenis permintaan yang berbeda tersebut, biasanya seorang backend developer membuat dua buah api yang berbeda, dimana satu api untuk di consume oleh website, sedangkan satunya lagi untuk mobile. Tidak ada yang salah dengan cara seperti ini, hanya saja jika aplikasi yang dibuat sudah sangat besar akan tetapi jumlah backend developer yang sedikit, maka imbasnya adalah aplikasi yang dibuat semakin lama untuk dirilis ke client karena harus membuat api tersendiri untuk masing-masing platform. Nah, jika hal semacam ini terjadi maka solusinya adalah dengan menggunakan GraphQL. Apa itu GraphQL? Kita bahas lebih detail.

GraphQL adalah bahasa query (query language) untuk sebuah API yang dibuat oleh Facebook. Bahasa query yang dimaksudkan disini bukanlah seperti query database yang sering anda jumpai di SQL maupun NoSQL. Query yang dimaksudkan disini adalah data dari API server backend, bisa dilakukan filterisasi langsung oleh client (web browser ataupun mobile) sesuai yang diinginkan oleh client melalui sebuah query yang dikirimkan ke server. Inilah yang merupakan kelebihan dari GraphQL jika dibandingkan dengan REST yang sering kita gunakan.

Supaya lebih mantap pemahaman mengenai GraphQL ini, maka kita akan langsung praktekkan bagaimana pengkodeannya di sisi backend. Disini saya akan mencontohkan membuat GraphQL server menggunakan bahasa golang dengan echo framework sebagai webserver dan gorm sebagai library database.

Membuat Database

Pertama kita buat sebuah database baru `nama terserah anda` kemudian buat tabel dengan script sql dibawah ini :
CREATE TABLE `level_user` (
 `id_level` int(2) NOT NULL AUTO_INCREMENT,
 `nama` varchar(100) NOT NULL,
 PRIMARY KEY (`id_level`)
);

CREATE TABLE `users` (
 `email` varchar(100) NOT NULL,
 `nama` varchar(50) NOT NULL,
 `no_handphone` varchar(15) DEFAULT NULL,
 `alamat` text DEFAULT NULL,
 `ktp` varchar(255) DEFAULT NULL,
 `id_level` int(2) NOT NULL,
 PRIMARY KEY (`email`) USING BTREE
);

INSERT INTO `level_user` (`id_level`, `nama`) VALUES
	(1, 'Super Admin'),
	(2, 'Admin');

INSERT INTO `users` (`email`, `nama`, `no_handphone`, `alamat`, `ktp`, `id_level`) VALUES
	(fulan@gmail.com', 'Ahmad Zainuddin', '0853xxxxx', 'Jalan Kemana', '', 1),
	('siapa@gmail.com', Zubayr, '08xxxx', 'Jalan - ', 'ktp.jpg', 2),
	(saya@gmail.com', Zakaria, '08xxxx', 'Jalan - ', 'ktp.jpg', 2);


Setup Project Baru Golang

Buat sebuah project baru di golang dan jalankan perintah berikut di terminal : go mod init mygraphql Setelah itu tambahkan modul dibawah ini :
go get github.com/labstack/echo/v4
go get gorm.io/gorm
go get gorm.io/driver/mysql
go get github.com/graphql-go/graphql 
go get github.com/graphql-go/handler
Didalam project yang sudah dibuat tadi, buat beberapa direktori baru dengan nama seperti dibawah ini :
setup project golang

Konfigurasi Database

Setelah selesai membuat project golang. Kemudian kita buat konfigurasi database didalam direktori config, buat sebuah file dengan nama database.go. Kemudian isi dengan code dibawah ini :
package config

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

var DB *gorm.DB

func ConnectDB() {
    host := "localhost"
    port := "3306"
    dbname := "blogspot"
    username := "root"
    password := ""

    dsn := username + ":" + password + "@tcp(" + host + ":" + port + ")/" + dbname + "?charset=utf8&parseTime=true&loc=Local"
    var err error
    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{SkipDefaultTransaction: true})
    if err != nil {
        panic("Cannot connect database")
    }
    DB.AutoMigrate()
}
Sesuaikan konfigurasi diatas dengan server MySQL yang berjalan di komputer anda.

Membuat Model

Di direktori model yang sudah dibuat tadi, buat 2 buah file baru yaitu `level.go` dan `users.go`. Isi masing-masing file dengan code dibawah ini :
level.go
package model

import (
    "mygraphql/config"

    "gorm.io/gorm"
)

type LevelUser struct {
    IdLevel int    `json:"id_level"`
    Nama    string `json:"nama"`
}

func (LevelUser) TableName() string {
    return "level_user"
}

func GetLevel(idLevel int) ([]LevelUser, error) {
    levels := new([]LevelUser)
    var result *gorm.DB
    if idLevel != 0 {
        result = config.DB.Where("id_level = ? ", idLevel).Find(levels)
    } else {
        result = config.DB.Find(levels)
    }
    return *levels, result.Error
}

users.go
package model

import "mygraphql/config"

type Users struct {
    Email       string `json:"email" form:"email" gorm:"primaryKey"`
    Nama        string `json:"nama" form:"nama"`
    NoHandphone string `json:"no_handphone" form:"no_handphone"`
    Alamat      string `json:"alamat" form:"alamat"`
    Ktp         string `json:"ktp" form:"ktp"`
}

func GetUsers(keywords string) ([]Users, error) {
    var users []Users
    result := config.DB.Where("email LIKE ? OR nama LIKE ?", "%"+keywords+"%", "%"+keywords+"%").Find(&users)

    return users, result.Error
}

Kedua file diatas kita gunakan untuk mendefinisikan fungsi yang akan mengambil data dari database yang sudah kita buat sebelumnya. Untuk penjelasan mengenai ini lebih jelasnya bisa anda baca dipostingan saya mengenai Crud API di Golang Menggunakan Echo Framework dan GORM.

Setup GraphQL

Nah, sekarang kita masuk ke bagian inti dari postingan ini, yaitu berkaitan dengan graphql itu tadi. Di direktori `graphql` buat dua buah file baru yaitu `query.go` dan `handler.go`.
query.go
package graphql

import (
    "mygraphql/model"

    "github.com/graphql-go/graphql"
)

var (
    level = graphql.NewObject(
        graphql.ObjectConfig{
            Name: "Level",
            Fields: graphql.Fields{
                "id_level": &graphql.Field{Type: graphql.ID},
                "nama":     &graphql.Field{Type: graphql.String},
            },
            Description: "Data level",
        },
    )
    user = graphql.NewObject(
        graphql.ObjectConfig{
            Name: "User",
            Fields: graphql.Fields{
                "email":        &graphql.Field{Type: graphql.ID},
                "nama":         &graphql.Field{Type: graphql.String},
                "no_handphone": &graphql.Field{Type: graphql.String},
                "alamat":       &graphql.Field{Type: graphql.String},
                "ktp":          &graphql.Field{Type: graphql.String},
            },
            Description: "Data users",
        },
    )
)

func levelData() *graphql.Field {
    return &graphql.Field{
        Type: graphql.NewList(level),
        Resolve: func(p graphql.ResolveParams) (interface{}, error) {
            idLevel, _ := p.Args["id_level"].(int)
            listLevel, err := model.GetLevel(idLevel)
            return listLevel, err
        },
        Description: "List Level",
        Args: graphql.FieldConfigArgument{
            "id_level": &graphql.ArgumentConfig{Type: graphql.Int},
        },
    }
}

func usersData() *graphql.Field {
    return &graphql.Field{
        Type: graphql.NewList(user),
        Resolve: func(p graphql.ResolveParams) (interface{}, error) {
            keywords, _ := p.Args["keywords"].(string)
            listUser, err := model.GetUsers(keywords)
            return listUser, err
        },
        Description: "List User",
        Args: graphql.FieldConfigArgument{
            "keywords": &graphql.ArgumentConfig{Type: graphql.String},
        },
    }
}

func newQuery() *graphql.Object {
    return graphql.NewObject(graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
            "users": usersData(),
            "level": levelData(),
        },
    })
}

Penjelasan :
  • Di file ini pertama kita deklarasikan terlebih dahulu variabel level dan user. Dimana di variabel tersebut diisi dengan fungsi NewObject dari graphql. Di variabel ini kita juga mendefinisikan field apa saja yang tersedia.
  • Beralih ke fungsi levelData dan usersData dimana fungsi ini memiliki return type berupa struct *graphql.Field.
  • Type: graphql.NewList(ofType graphql.Type) kita masukkan variabel `level` ataupun `users` yang sudah dibuat sebelumnya.
    Resolve: func(p graphql.ResolveParams) (interface{}, error) {}. Berisi data dari database tadi yang akan kita kembalikan ke client. Sebenarnya data yang di return disini bebas tidak hanya terbatas dari database saja, melainkan dari sumber lainnya pun boleh dilakukan.
    Args: graphql.FieldConfigArgument{}. Jika data membutuhkan filter di client, maka kita bisa mendifinisikannya dibagian ini.
  • Terakhir pada fungsi newQuery dimana pada fungsi ini kita jabarkan query yang bisa di panggil oleh client nantinya. Seperti yang terlihat di source code, kita mendaftarkan dua buah field data yang bisa di panggil oleh client. Yang pertama users dan yang kedua adalah level. Jadi dengan menggunakan field tersebutlah kita akan melakukan query data di client.

handler.go
package graphql

import (
    "github.com/graphql-go/graphql"
    "github.com/graphql-go/handler"
    "gorm.io/gorm"
)

func NewHandler() (*handler.Handler, error) {
    schema, err := graphql.NewSchema(
        graphql.SchemaConfig{
            Query: newQuery(),
        },
    )

    if err != nil {
        return nil, err
    }

    return handler.New(&handler.Config{
        Schema:   &schema,
        Pretty:   true,
        GraphiQL: true,
    }), nil
}

Penjelasan :
  • Dibagian handler kita buat sebuah fungsi NewHandler, dimana fungsi ini nantinya akan terhubung ke web server atau router dari echo. Terlihat dari baris selanjutnya kita definisikan schema yang sudah dibuat tadi dimana isinya adalah fungsi newQuery yang sudah dibuat sebelumnya di file `query.go` tadi.
  • Setelah selesai membuat file handler.go, selanjutnya yang terakhir adalah mendaftarkan handler tersebut ke router echo yang akan kita buat di file `server.go`. Isi dari file server.go adalah seperti dibawah ini :

server.go
package main

import (
    "fmt"
    "mygraphql/config"
    "mygraphql/graphql"

    "github.com/labstack/echo/v4"
)

func main() {
    config.ConnectDB()
    route := echo.New()

    h, err := graphql.NewHandler()
    if err != nil {
        fmt.Println(err)
    }
    route.POST("/graphql", echo.WrapHandler(h))
    route.Start(":9000")
}

type Response struct {
    ErrorCode int         `json:"error_code" form:"error_code"`
    Message   string      `json:"message" form:"message"`
    Data      interface{} `json:"data"`
}

Jika anda mengikuti postingan saya mengenai Rest API di Golang Menggunakan Echo Framework, disana sebagian besar sudah saya jelaskan mengenai penggunaan echo sebagai web server. Pada postingan ini yang perlu anda perhatikan hanya pada bagian :
h, err := graphql.NewHandler()
if err != nil {
    fmt.Println(err)
}
route.POST("/graphql", echo.WrapHandler(h))

Di bagian ini kita melakukan pemanggilan fungsi NewHandler yang sebelumnya sudah dibuat pada file `handler.go`. Kemudian pada baris akhir kita buat sebuah route dimana route tersebut adalah method post dengan url /graphql.

Setelah selesai, terakhir kita akan melakukan uji coba terhadap api graphql yang sudah dibuat. Disini saya menggunakan postman, atau anda boleh menggunakan tools lain yang biasa anda gunakan. Berikut adalah hasil pengujiannya :

  1. Pengujian dengan memanggil seluruh field yang tersedia di api graphql
  2. Pengujian dengan memanggil seluruh field yang tersedia di api graphql
  3. Pengujian dengan menggunakan memasukkan field yang spesifik
  4. Pengujian dengan menggunakan memasukkan field yang spesifik
  5. Pengujian dengan menggunakan filter dan field yang spesifik
  6. Pengujian dengan menggunakan filter dan field yang spesifik

Catatan :
Yang perlu anda perhatikan pada saat melakukan request data ke api graphql adalah query yang dikirim. Di postman sendiri sudah terdapat fitur graphql yang bisa anda gunakan. Output dari fitur graphql postman sendiri jika dilihat dalam bentuk curl contohnya adalah seperti dibawah ini :
curl --location --request POST 'http://localhost:9000/graphql' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query{\r\n    users (keywords:\"Ahmad\") {\r\n        email\r\n        nama\r\n        no_handphone\r\n    }\r\n    level (id_level:1){\r\n        id_level\r\n        nama\r\n    }\r\n}","variables":{}}'

Demikian postingan saya mengenai Belajar GraphQL, Serta Contoh Implementasinya di Golang, Jika ada pertanyaan silahkan ditanyakan dikolom komentar dibawah. Source code dari project ini sendiri bisa diakses melalui github di https://github.com/sardiabuzubayr/graphql-golang.

Demikian, semoga bermanfaat wassalamualaikum warahmatullah wabarakaatuh.
Share:

Dimanakah Sebaiknya Menyimpan Konfigurasi di Golang?

Dimanakah Sebaiknya Menyimpan Konfigurasi di Golang?
Bismillah

Setiap aplikasi yang kita buat tentu membutuhkan settingan konfigurasi. Konfigurasi tersebut pun bermacam-macam, bisa berupa konfigurasi database, email, url aplikasi dan lain-lain. Nah, lantas bagaimanakah sebaiknya settingan tersebut dilakukan? Apakah settingan tersebut langsung ditulis di dalam source code project anda? Tentu saja tidak jawabannya. Menulis langsung konfigurasi di dalam source code aplikasi anda akan membuat proses maintenance menjadi sangat sulit dilakukan. Bayangkan saja misalnya didalam source code anda terdapat konfigurasi yang berbeda-beda file-nya, satu ada di direktori ini dan yang lainnya tersebar di berbagai direktori lainnya. Dan ketika terjadi perubahan dengan konfigurasi itu, anda harus membongkar source code tersebut dan mencarinya satu persatu. Bayangkan pula model penulisan konfigurasi seperti ini anda buat bukan hanya satu project saja, tapi juga di project lainnya anda lakukan hal yang sama. Waah, pasti pusing tujuh keliling anda dibuatnya, hal seperti ini membutuhkan waktu yang lama dan melelahkan. Lalu apa solusinya? kita bahas lebih lanjut.

Umumnya konfigurasi sebuah aplikasi ditulis didalam file tersendiri, baik berupa file dengan eksistensi .env (environment), .properties, .json atau bentuk-bentuk lainnya. Yang jelas, file konfigurasi tersebut berada diluar source code aplikasi itu sendiri. Didalam postingan ini saya akan memberikan contoh bagaimana cara membuat file konfigurasi tersebut. Saya hanya mencontohkan cara membuat file konfigurasi tersebut menggunakan bahasa Go atau Golang. Untuk bahasa lainnya anda bisa mencari dari referensi lain, secara penerapan sama insyaAllah.

Ada beberapa library atau modul yang bisa anda gunakan di golang untuk menulis file konfigurasi diantaranya adalah modul godotenv atau juga bisa menggunakan vifer. Di tutorial ini saya mencontohkan file konfigurasi tersebut ditulis di file main.go, untuk project real, biasanya konfigurasi tersebut di buat di file khusus misalnya di config atau yang lainnya.
Baiklah langsung saja kita coba mempraktekkan bagaimana cara menggunakan modul tersebut satu persatu.

  1. GODOTENV
  2. Penggunaan library ini biasanya digunakan dengan eksistensi file .env. Untuk membuat konfigurasi menggunakan library ini, langkah-langkahnya adalah sebagai berikut :
    • Di project direktori anda, buka terminal/command line kemudian masukkan perintah :
    • go get github.com/joho/godotenv
    • Setelah selesai, buat sebuah file dengan eksistensi .env
    • Didalam file tersebut, tulis isi konfigurasi anda dengan format key-value, misalnya : APP-NAME=Belajar Konfigurasi Golang dimana APP-NAME adalah key, sedangkan Belajar Konfigurasi Golang merupakan value.
    • Selanjutnya buka file main.go jika sudah ada, atau jika belum buat file baru dengan nama main.go, kemudian masukkan source code seperti dibawah ini :
    • err := godotenv.Load(".env")
      if err != nil {
          panic("Cannot load .env file")
      }
      fmt.Println("Membuka konfigurasi menggunakan file .env")
      fmt.Println("==========================================")
      fmt.Println(os.Getenv("APP-NAME"))
      fmt.Println(os.Getenv("APP-GITHUB-PROJECT"))
      fmt.Println(os.Getenv("OWNER"))

      Penjelasan :
      • godotenv.Load(“.env”), artinya kita akan memuat atau membuka file .env tersebut, yang perlu diperhatikan adalah dimana anda menyimpan file .env tersebut, pastikan path dari file tersebut sudah benar karena jika path-nya salah maka akan terjadi error.
      • os.Getenv(“”), pada fungsi inilah kita menentukan key mana yang akan dipanggil.
  3. VIPER
  4. Viper sendiri lebih luas penggunaannya tidak berpatokan hanya satu jenis tipe file konfigurasi saja. Dengan viper anda bisa menulis konfigurasi dalam format JSON, TOML, YAML, HCL, INI, env, atau properties yang biasa di pakai di bahasa Java. Pada contoh ini kita akan mencoba menggunakan format JSON dan YAML. Adapun langkah-langkah untuk membuat konfigurasi menggunakan Viper adalah sebagai berikut :
    • Di project direktori anda, buka terminal/command line kemudian masukkan perintah :
    • go get github.com/spf13/viper
    • Kemudian, buat sebuah file dengan nama config.json
    • Tulis isi konfigurasi anda didalam file tersebut seperti menulis json pada umumnya, sebagai contoh seperti dibawah ini :
    • {
          "app_name":"Belajar Konfigurasi Golang",
          "app_github_project":"https://github.com/sardiabuzubayr",
          "owner":"Abu Zubayr",
          "port":3306
      }
    • Selanjutnya buka file main.go jika sudah ada, atau jika belum buat file baru dengan nama main.go, kemudian masukkan source code seperti dibawah ini :
    • viper.SetConfigName("config")
      viper.SetConfigType("json")
      viper.AddConfigPath(".")
      err = viper.ReadInConfig()
      if err != nil {
          panic("Cannot load configuration file")
      }
      fmt.Println("Membuka konfigurasi menggunakan file json (tanpa menentukan tipe data dari konfigurasi)")
      fmt.Println(viper.Get("app_name"))
      fmt.Println(viper.Get("app_github_project"))
      fmt.Println(viper.Get("owner"))
      fmt.Println(viper.Get("port"))
      
      fmt.Println("Membuka konfigurasi menggunakan file json (dengan menentukan tipe data dari konfigurasi)")
      fmt.Println(viper.GetString("app_name"))
      fmt.Println(viper.GetString("app_github_project"))
      fmt.Println(viper.GetString("owner"))
      fmt.Println(viper.GetInt("port"))

      Penjelasan :
      • viper.SetConfigName("config"), pada baris kode ini kita menentukan apa nama file konfigurasi yang akan dibaca oleh viper.
      • viper.SetConfigType("json"), bagian ini kita menentukan apa jenis file config yang kita gunakan.
      • viper.AddConfigPath("."), bagian ini menentukan dimana posisi file konfigurasi tersebut di tempatkan.
      • viper.ReadInConfig(), baris kode ini digunakan untuk memuat file konfigurasi tersebut, jika sudah benar nama, jenis, dan dimana file tersebut disimpan maka tidak akan terjadi error dibagian ini.
      • viper.Get(""), pada fungsi inilah kita tentukan key mana yang akan dipanggil. Fungsi ini bersifat global artinya seluruh data baik yang bertipe string atau angka bisa dipanggil dengan fungsi yang sama. Atau bisa juga dengan menggunakan fungsi yang lebih spesifik misalnya GetString untuk mengambil key yang datanya adalah karakter, atau GetInt untuk data yang bertipe integer, atau masih banyak lagi tipe yang disediakan oleh viper.

      Diatas adalah contoh tipe konfigurasi dengan file json. Untuk membuat dan membaca konfigurasi dengan file yaml, caranya sama, hanya saja format file konfigurasi ditulis mengikuti format yaml. Sebagai contoh adalah sebagai berikut :
      app_name: Belajar Konfigurasi Golang
      app_github_project: https://github.com/sardiabuzubayr
      owner: Abu Zubayr
      port: 3306

      Kemudian dibagian kode SetConfigType, masukkan “yaml” sehingga menjadi seperti viper.SetConfigType("yaml").
Source code keseluruhan terlihat seperti dibawah ini :
package main

import (
	"fmt"
	"os"

	"github.com/joho/godotenv"
	"github.com/spf13/viper"
)

func main() {
	err := godotenv.Load(".env")
	if err != nil {
		panic("Cannot load .env file")
	}
	fmt.Println("Membuka konfigurasi menggunakan file .env")
	fmt.Println("==========================================")
	fmt.Println(os.Getenv("APP-NAME"))
	fmt.Println(os.Getenv("APP-GITHUB-PROJECT"))
	fmt.Println(os.Getenv("OWNER"))

	viper.SetConfigName("config")
	viper.SetConfigType("json")
	viper.AddConfigPath(".")
	err = viper.ReadInConfig()
	if err != nil {
		panic("Cannot load configuration file " + err.Error())
	}
	fmt.Println("Membuka konfigurasi menggunakan file json (tanpa menentukan tipe data dari konfigurasi)")
	fmt.Println(viper.Get("app_name"))
	fmt.Println(viper.Get("app_github_project"))
	fmt.Println(viper.Get("owner"))
	fmt.Println(viper.Get("port"))

	fmt.Println("Membuka konfigurasi menggunakan file json (dengan menentukan tipe data dari konfigurasi)")
	fmt.Println(viper.GetString("app_name"))
	fmt.Println(viper.GetString("app_github_project"))
	fmt.Println(viper.GetString("owner"))
	fmt.Println(viper.GetInt("port"))

	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	err = viper.ReadInConfig()
	if err != nil {
		panic("Cannot load configuration file " + err.Error())
	}
	fmt.Println("Membuka konfigurasi menggunakan file yaml (tanpa menentukan tipe data dari konfigurasi)")
	fmt.Println(viper.Get("app_name"))
	fmt.Println(viper.Get("app_github_project"))
	fmt.Println(viper.Get("owner"))
	fmt.Println(viper.Get("port"))

	fmt.Println("Membuka konfigurasi menggunakan file yaml (dengan menentukan tipe data dari konfigurasi)")
	fmt.Println(viper.GetString("app_name"))
	fmt.Println(viper.GetString("app_github_project"))
	fmt.Println(viper.GetString("owner"))
	fmt.Println(viper.GetInt("port"))
}


Demikian tutorial singkat mengenai Dimanakah Sebaiknya Menyimpan Konfigurasi di GolangDimanakah Sebaiknya Menyimpan Konfigurasi di Golang. Jika ada pertanyaan silahkan komen dibawah postingan ini. Jika ada kesempatan, insyaAllah saya juga akan membahas mengenai Centralize Configuration yang juga berkaitan dengan materi yang sedang kita bahas sekarang ini. Project ini juga ada saya tulis di github yang bisa dilihat di https://github.com/sardiabuzubayr/go-config.

Semoga bermanfaat, wassalamualaikum warahmatullah wabarakatuh.
Share:

Crud API di Golang Menggunakan Echo Framework dan GORM

Crud API di Golang Menggunakan Echo Framework dan GORM

Bismillah...

Walhamdulillah, pada postingan sebelumnya kita sudah membahas mengenai Rest API di Golang Menggunakan Echo Framework. Pada postingan tersebut kita telah membahas tentang dasar-dasar pembuatan rest api di golang menggunakan framework yang diklaim lebih cepat dari Gin. Nah, postingan ini merupakan lanjutan dari postingan tersebut, jika anda belum membacanya sangat disarankan untuk mengikuti tutorial sebelumnya dari awal postingan.

Baiklah, di postingan ini kita akan mencoba membuat fungsi CRUD (Create, Read Update and Delete) sederhana menggunakan salah satu library ORM yang dipakai di golang yakni GORM. Untuk mengakses dokumentasi secara lengkap anda bisa menuju ke laman website resminya di https://gorm.io. Untuk studi kasus, kita akan membuat sebuah fungsi crud untuk tabel users. Dimana tabel tersebut berisi Email, Nama, No Handphone, Alamat dan KTP.

Buatlah sebuah database (nama database bebas) kemudian buat sebuah tabel dengan nama users. Untuk mempermudah anda bisa langsung copas script pembuatan tabel di bawah ini :
CREATE TABLE users (
	email VARCHAR(100) NOT NULL,
	nama VARCHAR(50) NOT NULL,
	no_handphone VARCHAR(15) DEFAULT NULL,
	alamat TEXT DEFAULT NULL,
	ktp VARCHAR(255) DEFAULT NULL,
	PRIMARY KEY (email) USING BTREE
);


Setelah selesai membuat tabel, langsung buka project sebelumnya. Beberapa yang akan kita lakukan adalah sebagai berikut :
  1. Membuat Koneksi Database
  2. Sebelum membuat konesi database, tambahkan terlebih dahulu modul GORM didalam project anda. Caranya adalah dengan memasukkan perintah di terminal/command line dengan :
    go get gorm.io gorm.io/driver/mysql

    Setelah selesai, buat terlebih dahulu sebuah direktori misalnya dengan nama config. Kemudian didalam direktori tersebut buat sebuah file dengan nama database.go. Isi file tersebut dengan kode seperti dibawah ini :
    package config
    
    import (
        "gorm.io/driver/mysql"
        "gorm.io/gorm"
    )
    
    var DB *gorm.DB
    
    func ConnectDB() {
        host := "localhost"
        port := "3306"
        dbname := "blogspot"
        username := "root"
        password := ""
    
        dsn := username + ":" + password + "@tcp(" + host + ":" + port + ")/" + dbname + "?charset=utf8&parseTime=true&loc=Local"
        var err error
        DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{SkipDefaultTransaction: true})
        if err != nil {
            panic("Cannot connect database")
        }
        DB.AutoMigrate()
    }

    Pada file database.go diatas, kita membuat sebuah variabel global dengan var DB *gorm.io. Variabel inilah yang nantinya akan kita gunakan untuk melakukan query ke database. Namun perlu diketahui bahwa cara penggunaan seperti ini tidak dianjurkan jika project yang dibuat dengan skala besar. Untuk project dengan skala besar disarankan untuk menerapkan clean arcitecture dimana koneksi database tidak di expose secara global, melainkan dibuat di masing-masing repository yang menyimpan database handler.

  3. Membuat Fungsi CRUD
  4. Selanjutnya kita akan membuat fungsi crud menggunakan koneksi database yang sudah dibuat sebelumnya. Supaya lebih teratur, maka fungsi crud tersebut akan saya buat terpisah dari file server.go. Untuk itu buat terlebih dahulu sebuah direktori baru lagi dengan nama model, kemudian buat sebuah file didalamnya dengan nama users.go. Didalam file ini kita akan memindahkan struct Users yang sebelumnya berada di file server.go ke file users.go. Isi dari file users.go adalah seperti dibawah ini :
    package model
    
    import "restapi/config"
    
    type Users struct {
        Email       string `json:"email" form:"email" gorm:"primaryKey"`
        Nama        string `json:"nama" form:"nama"`
        NoHandphone string `json:"no_handphone" form:"no_handphone"`
        Alamat      string `json:"alamat" form:"alamat"`
        Ktp         string `json:"ktp" form:"ktp"`
    }
    
    func (user *Users) CreateUser() error {
        if err := config.DB.Create(user).Error; err != nil {
            return err
        }
        return nil
    }
    
    func (user *Users) UpdateUser(email string) error {
        if err := config.DB.Model(&Users{}).Where("email = ?", email).Updates(user).Error; err != nil {
            return err
        }
        return nil
    }
    
    func (user *Users) DeleteUser() error {
        if err := config.DB.Delete(user).Error; err != nil {
            return err
        }
        return nil
    }
    
    func GetOneByEmail(email string) (Users, error) {
        var user Users
        result := config.DB.Where("email = ?", email).First(&user)
        return user, result.Error
    }
    
    func GetAll(keywords string) ([]Users, error) {
        var users []Users
        result := config.DB.Where("email LIKE ? OR nama LIKE ?", "%"+keywords+"%", "%"+keywords+"%").Find(&users)
    
        return users, result.Error
    }

    Penjelasan :
    • CreateUser
    • Method ini adalah method yang kita gunakan untuk melakukan insert ke database. Pada method ini memiliki penerima berupa struct Users. Artinya method CreateUser melekat pada struct Users, dan bisa diakses langsung oleh object struct tersebut. Kemudian method ini juga memiliki nilai balik atau return value berupa interface error. Method yang digunakan untuk insert data ke database melalui gorm adalah method Create, seperti yang terlihat di baris kode config.DB.Create(user). config.DB sendiri merupakan variabel koneksi database yang sebelumnya sudah kita buat di file database.go.
    • UpdateUser
    • Method ini merupakan method yang kita gunakan untuk melakukan update data ke database. Sama seperti method CreateUser, method ini juga memiliki penerima berupa struct Users. Kemudian method ini memiliki parameter berupa primary key dari tabel users yakni email. Jadi nantinya kita akan melakukan update data users berdasarkan email yang dimiliki oleh user tersebut. Yang perlu diperhatikan adalah pada bagian config.DB.Model(&Users), ini menandakan bahwa kita akan melakukan sesuatu di model atau tabel users. Kemudian dilanjutkan dengan method Where(“email = ?”, email) dimana ini merupakan filter user yang memiliki email tersebut yang akan kita update data-nya, dan dilanjutkan dengan method Updates(user) dimana method inilah yang akan melakukan perintah eksekusi update ke database.
    • DeleteUser
    • Kemudian method terakhir yang akan kita buat adalah method UpdateUser. Dimana method ini yang digunakan untuk menghapus data yang ada pada tabel users. Letak eksekusi perintah delete terdapat pada bagian config.DB.Delete(user).
    • GetOneByEmail
    • Fungsi ini digunakan untuk mengambil data di tabel users berdasarkan email. Jadi seperti yang sudah terlihat di baris kode, bahwa fungsi ini menerima parameter berupa string email. Kemudian return dari fungsi ini adalah struct Users dan error. Jika anda belum familiar dengan fungsi yang seperti ini saya kira wajar, karena saya sendiri juga baru menemukan di golang bahwa sebuah method atau fungsi bisa memiliki return value lebih dari 1 object atau nilai. Method yang digunakan gorm untuk melakukan select sendiri adalah method First. Dimana method ini akan melakukan select dengan limit 1 baris.
    • GetAll
    • Kemudian fungsi terakhir adalah fungsi GetAll. Fungsi ini merupakan fungsi yang akan kita gunakan untuk menampilkan data secara keseluruhan atau lebih dari 1 baris. Pada method ini juga saya masukkan sebuah parameter keywords, dimana parameter tersebut digunakan untuk melakukan filter dari query yang kita jalankan. Untuk return sendiri, fungsi ini memiliki 2 buah return value yakni array dari struct Users dan error.

  5. Mengubah Router
  6. Setelah selesai membuat method dan fungsi diatas, selanjutnya adalah mengubah bagian router di file server.go yang telah kita buat sebelumnya dengan kode seperti dibawah ini :
    config.ConnectDB()
    route := echo.New()
    route.POST("user/create_user", func(c echo.Context) error {
    	user := new(model.Users)
    	c.Bind(user)
    	contentType := c.Request().Header.Get("Content-type")
    	if contentType == "application/json" {
    		fmt.Println("Request dari json")
    	} else if strings.Contains(contentType, "multipart/form-data") || contentType == "application/x-www-form-urlencoded" {
    		file, err := c.FormFile("ktp")
    		if err != nil {
    			fmt.Println("Ktp kosong")
    		} else {
    			src, err := file.Open()
    			if err != nil {
    				return err
    			}
    			defer src.Close()
    			dst, err := os.Create(file.Filename)
    			if err != nil {
    				return err
    			}
    			defer dst.Close()
    			if _, err = io.Copy(dst, src); err != nil {
    				return err
    			}
    
    			user.Ktp = file.Filename
    			fmt.Println("Ada file, akan disimpan")
    		}
    	}
    	response := new(Response)
    	if user.CreateUser() != nil { // method create user
    		response.ErrorCode = 10
    		response.Message = "Gagal create data user"
    	} else {
    		response.ErrorCode = 0
    		response.Message = "Sukses create data user"
    		response.Data = *user
    	}
    	return c.JSON(http.StatusOK, response)
    })
    
    route.PUT("user/update_user/:email", func(c echo.Context) error {
    	user := new(model.Users)
    	c.Bind(user)
    	response := new(Response)
    	if user.UpdateUser(c.Param("email")) != nil { // method update user
    		response.ErrorCode = 10
    		response.Message = "Gagal update data user"
    	} else {
    		response.ErrorCode = 0
    		response.Message = "Sukses update data user"
    		response.Data = *user
    	}
    	return c.JSON(http.StatusOK, response)
    })
    
    route.DELETE("user/delete_user/:email", func(c echo.Context) error {
    	user, _ := model.GetOneByEmail(c.Param("email")) // method get by email
    	response := new(Response)
    
    	if user.DeleteUser() != nil { // method update user
    		response.ErrorCode = 10
    		response.Message = "Gagal menghapus data user"
    	} else {
    		response.ErrorCode = 0
    		response.Message = "Sukses menghapus data user"
    	}
    	return c.JSON(http.StatusOK, response)
    })
    
    route.GET("user/search_user", func(c echo.Context) error {
    	response := new(Response)
    	users, err := model.GetAll(c.QueryParam("keywords")) // method get all
    	if err != nil {
    		response.ErrorCode = 10
    		response.Message = "Gagal melihat data user"
    	} else {
    		response.ErrorCode = 0
    		response.Message = "Sukses melihat data user"
    		response.Data = users
    	}
    	return c.JSON(http.StatusOK, response)
    })
    
    route.Start(":9000")

    Di baris kode diatas, saya membuat sebuah struct Response. Isinya adalah sebagai berikut :
    type Response struct {
        ErrorCode int         `json:"error_code" form:"error_code"`
        Message   string      `json:"message" form:"message"`
        Data      interface{} `json:"data"`
    }

    Pada kode diatas, yang perlu anda perhatikan adalah pada bagian yang saya beri tanda komentar //. Pada bagian tersebutlah merupakan method dan fungsi yang kita panggil dari file users.go.
    Setelah selesai seluruhnya, sekarang kita lakukan pengujian terhadap api yang sudah dibuat menggunakan postman.
    1. Endpoint (user/create_user) POST
    2. Pengujian Rest Api Golang dengan Request multipart/form-data
      Pengujian menggunakan Content-Type : multipart/form-data
      Pengujian Rest Api Golang dengan Request application/json
      Pengujian menggunakan Content-Type : application/json

    3. Endpoint (user/update_user/:email) PUT
    4. Pengujian Rest Api Golang method PUT

    5. Endpoint (user/delete_user) DELETE
    6. Pengujian Rest Api Golang method DELETE

    7. Endpoint (user/search_user) GET
    8. Pengujian Rest Api Golang method GET tanpa parameter
      Request tanpa query params
      Pengujian Rest Api Golang method GET dengan query parameter
      Request dengan query params

Demikian tutorial Crud API di Golang Menggunakan Echo Framework dan GORM. Jika ada pertanyaan silahkan ditanyakan dikolom komentar dibawah. Untuk project juga sudah saya post di github bisa di cek di https://github.com/sardiabuzubayr/restapi-gorm.
Semoga bermanfaat, wassalamualaikum warahmatullah wabarakatuh
Share:

Rest API di Golang Menggunakan Echo Framework

Rest API di Golang Menggunakan Echo Framework
Bismillah...
Perkembangan teknologi dewasa ini sangatlah cepat, dulu kita sering menggunakan Soap sebagai protokol untuk pertukaran data lintas platform, akan tetapi seiiring berjalannya waktu, protokol tersebut pelan-pelan mulai ditinggalkan karena kemunculan protokol baru untuk pertukaran data yakni Rest API yang menawarkan kecepatan yang lebih baik dari pendahulunya.

Pada postingan ini saya akan membuat sebuah contoh penggunaan rest api dengan menggunakan bahasa golang, khususnya dengan menggunakan framework echo. Mengapa saya menggunakan golang untuk membuat rest api ini? Jawabannya karena saya sedang mempelajari bahasa golang, hhhee. Saya tertarik mempelajari bahasa golang karena dari segi kecepatannya sangat luar biasa cepat jika dibandingkan dengan bahasa lain. Sebelumnya saya pernah membandingkan rest api golang dengan php (framework Lumen). Hasilnya sangat beda jauh dari segi kecepatan. Pada simple case, lumen memerlukan 100 - 200 ms untuk merespon sebuah permintaan dan mengembalikan dalam bentuk json. Akan tetapi golang hanya memerlukan waktu 3 - 10 ms untuk untuk merespon permintaan tersebut. Kemudian dengan menggunakan J-Meter untuk test permintaan secara concurent atau request secara bersamaan (1000 - 2000 request secara bersamaan), hasil yang didapat semakin jauh berbeda. Lumen memerlukan 6000 an ms atau 6 second keatas, akan tetapi golang masih dibawah 1 second. Luarrr biasa, hal inilah yang mendorong saya untuk mempelajari lebih lanjut mengenai golang dan tentu harapan kedepannya adalah menggunakan golang ini sebagai bahasa utama saya disisi backend.

Didalam bahasa golang sendiri, secara default sudah menyediakan sebuah package net/http. Didalam package tersebut sudah tersedia untuk  web server, routing, templating dan lainnya. Akan tetapi untuk lebih mempermudah saya menggunakan framework tambahan yaitu echo framework. Dokumentasi dari framework ini secara lengkap bisa diakses di https://echo.labstack.com. Echo sendiri mengklaim bahwa dirinya lebih baik dari Gin framework yang juga merupakan framework untuk develop website di go. Ini bisa dilihat dari github echo framework di https://github.com/labstack/echo.

Baiklah langsung saja, ditulisan ini saya akan memasukkan beberapa hal yang umum kita gunakan di rest api seperti POST, PUT, GET dan DELETE. Disini saya tidak memasukkan data yang dikirim kedalam database. Akan tetapi hanya sekedar mengirim dan membaca data secara statis saja. InsyaAllah pada kesempatan lain saya akan membuat rest api ini lengkap dengan database, yassarallahu li.

Disini saya berasumsi anda sudah menginstal golang, jika belum bisa menginstal langsung dari website resmi golang di https://go.dev . Baiklah jika sudah terinstall di laptop atau pc anda kita akan langsung mulai :
  1. Buat sebuah direktori/folder beri saja namanya restapi misalnya
  2. Kemudian buka terminal atau command line anda dan masukkan perintah go mod init restapi
  3. go mod init restapi
    Dari perintah di atas akan menghasilkan sebuah file go.mod. Didalam file ini seluruh modul yang akan kita gunakan akan di definisikan di file tersebut.
  4. Setelah itu tambahkan modul echo framework dengan perintah :
  5. go get github.com/labstack/echo/v4
    Perintah diatas kita gunakan untuk menambahkan sebuah modul baru yakni modul echo framework tadi.
  6. Selanjutnya buka editor kesayangan anda, disini saya menggunakan Visual Studio Code. Didalam project directory anda, buat sebuah file dengan nama server.go.
  7. Didalam file server.go kita akan membuat sebuah struct yang memuat entitas user seperti email, nama, no_handphone, alamat dan ktp. Berikut adalah isi dari struct tersebut :
  8. type Users struct {
        Email       string `json:"email" form:"email"`
        Nama        string `json:"nama" form:"nama"`
        NoHandphone string `json:"no_handphone" form:"no_handphone"`
        ALamat      string `json:"alamat" form:"alamat"`
        Ktp         string `json:"ktp" form:"ktp"`
    }
    Pada struct diatas, kita sudah mendefinisikan entitas user. Yang perlu anda perhatikan adalah pada keyword json dan form. Keyword diatas berarti kita menentukan nama variabel yang digunakan untuk pertukaran data di rest api nantinya. Dimana keyword json ini akan terbaca pada saat data yang kita kirimkan dengan Content-Type application/json. Kemudian form akan terbaca jika Content-Type yang dikirim berupa multipart/form-data atau application/x-www-form-urlencoded.
  9. Kemudian di file yang sama, kita akan menginisialisasi fungsi dari framework echo yang akan kita gunakan sebagai web server sekaligus routing dari api yang kita buat. Tambahkan baris kode dibawah ini :
  10. func main() {
    	route := echo.New()
    	route.POST("user/create_user", func(c echo.Context) error {
    		…
    	})
    	route.PUT("user/update_user/:email", func(c echo.Context) error {
    		…
    	})
    	route.DELETE("user/delete_user/:email", func(c echo.Context) error {
    		…
    	})
    	route.GET("user/search_user", func(c echo.Context) error {
    		…
    	})
    	route.Start(":9000")
    }
    Pada source code diatas di function main, kita deklarasikan sebuah variabel route dengan inisialisasi function echo.New(). Function tersebut merupakan instance dari framework echo. Kemudian dilanjutkan kita definisikan method-method seperti POST, PUT, DELETE dan GET yang selanjutnya akan kita tulis kode didalamnya satu persatu.
  11. Selanjutnya dibagian method POST kita akan definisikan bahwa method tersebut akan menerima request dalam bentuk Content-Type application/json ataupun multipart/form-data. Sehingga baris kode pada method tersebut menjadi seperti berikut ini :
  12. 	user := new(Users)
            c.Bind(user)
            contentType := c.Request().Header.Get("Content-type")
            if contentType == "application/json" {
                fmt.Println("Request dari json")
            } else if strings.Contains(contentType, "multipart/form-data") || contentType == "application/x-www-form-urlencoded" {
                file, err := c.FormFile("ktp")
                if err != nil {
                    fmt.Println("Ktp kosong")
                } else {
                    src, err := file.Open()
                    if err != nil {
                        return err
                    }
                    defer src.Close()
                    dst, err := os.Create(file.Filename)
                    if err != nil {
                        return err
                    }
                    defer dst.Close()
                    if _, err = io.Copy(dst, src); err != nil {
                        return err
                    }
    
                    user.Ktp = file.Filename
                    fmt.Println("Ada file, akan disimpan")
                }
            }
            response := struct {
                Message string
                Data    Users
            }{
                Message: "Sukses melakukan penambahan data",
                Data:    *user,
            }
            return c.JSON(http.StatusOK, response)
    Penjelasan :
    • user := new(Users). Keyword new merupakan instance dari sebuah struct dalam hal ini Users. Variabel user menampung data pointer bertipe struct User.
      c.Bind(user) . Merupakan fungsi yang disediakan oleh framework echo untuk melakukan binding field yang dikirim oleh client kedalam sebuah interface.
      Cara diatas juga bisa diganti dengan kode seperti ini :
      var user Users
      c.Bind(&user)
    • contentType := c.Request().Header.Get("Content-type") digunakan untuk mengetahui jenis Content-Type apa yang dikirim oleh client.
    • Pada bagian selanjutnya hanya berupa pengecekan apa jenis content-type tadi. Kemudian khusus bagian :
      else if strings.Contains(contentType, "multipart/form-data") || contentType == "application/x-www-form-urlencoded" { …. } didalam blok kode tersebut saya mengecek apakah ada file yang dikirim, karena untuk Content-Type json tidak bisa mengirim data dalam bentuk file. Jika ada file yang dikirim, maka akan saya simpan file tersebut. Selanjutnya nama file yang dikirim tadi akan saya masukkan ke variabel ktp.
    • return c.JSON(http.StatusOK, user) merupakan response yang akan dikembalikan ke client yakni dalam bentuk json dari struct user tadi.
      http.StatusOK merupakan response code 200 yang dikembalikan ke client.
  13. Beralih ke method selanjutnya yakni method PUT. Pada method PUT ini umumnya digunakan untuk pengupdatan/perubahan data. Pada method ini masukkan kode berikut :
  14. 	user := new(Users)
            c.Bind(user)
            user.Email = c.Param("email")
            // do something here ....
            response := struct {
                Message string
                Data    Users
            }{
                Message: "Sukses mengupdate data",
                Data:    *user,
            }
            return c.JSON(http.StatusOK, response)
    Penjelasan :
    • c.Param(“email”) digunakan untuk mengambil parameter dari url. Dalam hal ini parameter yang ada di method PUT tersebut adalah email.
  15. Kemudian dibagian method DELETE. Method ini digunakan untuk melakukan penghapusan data. Sama seperti method PUT, biasanya method DELETE juga menyertakan sebuah ID untuk identifikasi data mana yang akan dihapus. Isi dari method DELETE seperti berikut ini :
  16. 	user := new(Users)
            user.Email = c.Param("email")
            // do something here ....
            response := struct {
                Message string
                ID      string
            }{
                Message: "Sukses menghapus data",
                ID:      user.Email,
            }
            return c.JSON(http.StatusOK, response)
  17. Terakhir adalah method GET. Method ini biasanya digunakan untuk melakukan request data ke server. Dan biasanya method ini juga mengirimkan sebuah query parameter. Berikut adalah contohnya :
  18. 	user := new(Users)
            user.Email = c.QueryParam("keywords")
            user.Nama = "Abu Zubayr"
            user.ALamat = "Jalan Jalan"
            user.Ktp = "file.jpg"
            // do something here ....
            response := struct {
                Message string
                Data    Users
            }{
                Message: "Sukses melihat data",
                Data:    *user,
            }
            return c.JSON(http.StatusOK, response)
  19. Setelah selesai menulis seluruhnya. Saatnya melakukan pengujian menggunakan Postman. Terlebih dahulu, jalankan server anda dengan membuka command line lalu masukkan perintah go run server.go

Hasil pengujian rest api adalah sebagai berikut :

  1. Endpoint (user/create_user) POST
  2. Endpoint (user/create_user) POST json
    - Diatas adalah contoh request post method dengan data yang dikirim dalam bentuk json (Content-Type : application/json). Kemudian jika menggunakan Content-Type : multipart/form atau application/x-www-form-urlencoded, maka request-nya adalah seperti gambar dibawah ini :
    Endpoint (user/create_user) POST form
    - Diatas pada field ktp diisi terisi sebuah file yang dikirim. Untuk pengiriman file sendiri, tidak bisa dilakukan dengan Content-Type : application/json.
  3. Endpoint (user/update_user/:email) PUT
  4. Endpoint (user/update_user/:email) PUT
    - Terlihat di endpoint method diatas terdapat parameter email fulan@gmail.com. Parameter inilah yang dibaca go dengan kode c.Param(“email”).
  5. Endpoint (user/delete_user/:email) DELETE
  6. Endpoint (user/delete_user/:email) DELETE
  7. Endpoint (user/search_user) GET
  8. Endpoint (user/search_user) GET
Demikian, hasil yang didapat dari pengujian api menggunakan postman. Untuk source code full dari aplikasi diatas adalah sebagai berikut :
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"

    "github.com/labstack/echo/v4"
)

type Users struct {
    Email       string `json:"email" form:"email"`
    Nama        string `json:"nama" form:"nama"`
    NoHandphone string `json:"no_handphone" form:"no_handphone"`
    ALamat      string `json:"alamat" form:"alamat"`
    Ktp         string `json:"ktp" form:"ktp"`
}

func main() {
    route := echo.New()
    route.POST("user/create_user", func(c echo.Context) error {
        user := new(Users)
        c.Bind(user)
        contentType := c.Request().Header.Get("Content-type")
        if contentType == "application/json" {
            fmt.Println("Request dari json")
        } else if strings.Contains(contentType, "multipart/form-data") || contentType == "application/x-www-form-urlencoded" {
            file, err := c.FormFile("ktp")
            if err != nil {
                fmt.Println("Ktp kosong")
            } else {
                src, err := file.Open()
                if err != nil {
                    return err
                }
                defer src.Close()
                dst, err := os.Create(file.Filename)
                if err != nil {
                    return err
                }
                defer dst.Close()
                if _, err = io.Copy(dst, src); err != nil {
                    return err
                }

                user.Ktp = file.Filename
                fmt.Println("Ada file, akan disimpan")
            }
        }
        response := struct {
            Message string
            Data    Users
        }{
            Message: "Sukses melakukan penambahan data",
            Data:    *user,
        }
        return c.JSON(http.StatusOK, response)
    })

    route.PUT("user/update_user/:email", func(c echo.Context) error {
        user := new(Users)
        c.Bind(user)
        user.Email = c.Param("email")
        // do something here ....
        response := struct {
            Message string
            Data    Users
        }{
            Message: "Sukses mengupdate data",
            Data:    *user,
        }
        return c.JSON(http.StatusOK, response)
    })

    route.DELETE("user/delete_user/:email", func(c echo.Context) error {
        user := new(Users)
        user.Email = c.Param("email")
        // do something here ....
        response := struct {
            Message string
            ID      string
        }{
            Message: "Sukses menghapus data",
            ID:      user.Email,
        }
        return c.JSON(http.StatusOK, response)
    })

    route.GET("user/search_user", func(c echo.Context) error {
        user := new(Users)
        user.Email = c.QueryParam("keywords")
        user.Nama = "Abu Zubayr"
        user.ALamat = "Jalan Jalan"
        user.Ktp = "file.jpg"
        // do something here ....
        response := struct {
            Message string
            Data    Users
        }{
            Message: "Sukses melihat data",
            Data:    *user,
        }
        return c.JSON(http.StatusOK, response)
    })

    route.Start(":9000")
}

Untuk tutorial dasar Rest API di Golang Menggunakan Echo Framework cukup sampai disini. Untuk project ini sendiri juga sudah saya post di github bisa di cek di https://github.com/sardiabuzubayr/simple-echo-restapi.
Semoga bermanfaat, wassalamualaikum warahmatullah wabarakatuh
Share:
DigitalOcean Referral Badge
www.domainesia.com
Powered by Blogger.

Labels