Belajar Golang – Membuat API Dengan Golang dan MySQL

Dipublikasikan oleh El Cicko pada

api dengan golang dan mysql

Pada artikel ini kali ini kita akan belajar Golang dengan contoh studi kasus bagaimana cara kita membuat API dengan Golang dan MySQL. Pada contoh API dengan Golang kali ini, kita akan belajar bagaimana menyediakan sebuah endpoints untuk mengakses dan memanipulasi data user.

Konsep API Dengan Golang

Pertama-tama kita akan membuat konsep route pada API yang akan kita buat :

  • Membuat data user baru dengan menggunakan method POST pada route /user.
  • Memperbaharui atau mengubah data user yang sudah ada dengan method PUT pada route /user/{id}.
  • Menghapus data user tertentu dengan method DELETE pada route /user/{id}.
  • Menampilkan data user tertentu dengan method GET pada route /user/{id}.
  • Menampilkan semua data user dengan method GET pada route /user.

Parameter {id} menandakan identitas dari data user yang kita olah.

Struktur Database Untuk API Dengan Golang

Kita akan membuat sebuah database sebagai contoh kasus dalam membuat API dengan Golang dan MySQL. Buat database pada MySQL dengan nama db_go lalu buat table user seperti pada query berikut.

CREATE DATABASE `db_go`
USE `db_go`;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Setelah selesai, kita masukan 3 buah data sebagai contoh :

INSERT INTO `users` (`id`, `name`) VALUES
(1, 'User A'),
(2, 'User B'),
(3, 'User C');

Ok, proses pembuatan database pada MySQL selesai.

Library Yang Dibutuhkan Untuk API Dengan Golang

Pada bagian ini saya asumsikan kamu sudah menginstall Go pada environment kamu, jika belum kamu dapat membaca terlebih dahulu tutorial saya sebelumnya tentang bagaimana cara melakukan instalasi Golang di Windows, MacOS dan Linux.

Sebelum memulai kita memerlukan sebuah dependencies atau library yang akan kita gunakan dalam membangun Rest API dengan Golang dan MySQL.

Setidaknya kita membutuhkan 2 buah library sebagai berikut :

  • muxGorilla Mux untuk kebutuhkan routing
  • mysqlDriver Golang yang digunakan untuk melakukan koneksi ke database MySQL

Kita dapat dengan mudah menginstall dependencies tersebut dengan mengetikan perintah berikut pada console.

go get github.com/gorilla/mux
go get github.com/go-sql-driver/mysql

Development API Dengan Golang

Kita sekarang masuk ke tahap development API dengan Golang. Pertama kita akan membuat file bernama app.go sebagai pondasi dari Rest API yang kita buat.

File app.go ini memuat konfigurasi yang kita butuhkan seperti route dan koneksi ke database MySQL.

// app.go

package main

import (
    "database/sql"
    "github.com/gorilla/mux"
    _ "github.com/go-sql-driver/mysql"
)

type App struct {
    Router *mux.Router
    DB     *sql.DB
}

func (a *App) Initialize(user, password, dbname string) { }

func (a *App) Run(addr string) { }

Method Initialize() bertanggung jawab untuk membangun koneksi ke database MySQL, sedangkan method Run() digunakan untuk menjalankan script Golang.

Pada skrip app.go kita juga melakukan import untuk package mux dan mysql.

Sekarang kita buat file baru dengan nama main.go. File ini digunakan sebagai script utama pada API dengan Golang yang kita buat.

// main.go

package main

func main() {
	a := App{}
	// konfigurasi database disini
	a.Initialize("DB_USERNAME", "DB_PASSWORD", "db_go")

	a.Run(":8080")
}

Pada file main.go kita memasukan konfigurasi untuk koneksi ke database MySQL.

Terakhir kita buat lagi satu file baru bernama model.go. File digunakan untuk mendefinisikan struktur model pada tabel user.

Pada file model.go ini juga kita akan menyisipkan beberapa fungsi yang dapat digunakan untuk membantu kita dalam melakukan operasi pada database. Sementara kita akan mengisi file tersebut seperti ini saja.

// model.go
package main

import (
	"database/sql"
	"errors"
)

// struktur tabel
type user struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

// menampilkan 1 user saja
func (u *user) getUser(db *sql.DB) error {
	return errors.New("Belum ada kode apapun.")
}

// memperbaharui data user
func (u *user) updateUser(db *sql.DB) error {
	return errors.New("Belum ada kode apapun.")
}

// menghapus data user
func (u *user) deleteUser(db *sql.DB) error {
	return errors.New("Belum ada kode apapun.")
}

// menyimpan user baru
func (u *user) createUser(db *sql.DB) error {
	return errors.New("Belum ada kode apapun.")
}

// menampilkan banyak user
func getUsers(db *sql.DB, start, count int) ([]user, error) {
	return nil, errors.New("Not implemented")
}

Sekarang kita memiliki 3 buah file yang terdiri dari app.go, main.go dan juga model.go.

┌── app.go
├── main.go
└── model.go

Operasi CRUD Pada API Dengan Golang

Sekarang kita akan membuat script untuk melakukan CRUD didalam API dengan Golang. Pertama-tama kita akan memodifikasi terlebih dahulu file model.go dengan memasukan berbagai fungsi yang kita butuhkan untuk dapat melakukan operasi dengan database.

// model.go
package main

import (
	"database/sql"
	"fmt"
)

// struktur tabel
type user struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

// menampilkan data untuk single row
func (u *user) getUser(db *sql.DB) error {
	statement := fmt.Sprintf("SELECT name FROM users WHERE id=%d", u.ID)
	return db.QueryRow(statement).Scan(&u.Name)
}

// memperbaharui data
func (u *user) updateUser(db *sql.DB) error {
	statement := fmt.Sprintf("UPDATE users SET name='%s' WHERE id=%d", u.Name, u.ID)
	_, err := db.Exec(statement)
	return err
}

// menghapus data
func (u *user) deleteUser(db *sql.DB) error {
	statement := fmt.Sprintf("DELETE FROM users WHERE id=%d", u.ID)
	_, err := db.Exec(statement)
	return err
}

// membuat data baru
func (u *user) createUser(db *sql.DB) error {
	statement := fmt.Sprintf("INSERT INTO users(name) VALUES('%s')", u.Name)
	_, err := db.Exec(statement)
	if err != nil {
		return err
	}
	err = db.QueryRow("SELECT LAST_INSERT_ID()").Scan(&u.ID)
	if err != nil {
		return err
	}
	return nil
}

// menampilkan data dalam multiple rows
func getUsers(db *sql.DB, start, count int) ([]user, error) {
	statement := fmt.Sprintf("SELECT id, name FROM users LIMIT %d OFFSET %d", count, start)
	rows, err := db.Query(statement)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	users := []user{}
	for rows.Next() {
		var u user
		if err := rows.Scan(&u.ID, &u.Name); err != nil {
			return nil, err
		}
		users = append(users, u)
	}
	return users, nil
}

Setelah itu kita akan memodifikasi file app.go untuk memasukkan fungsi pada setiap method.

// app.go

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

type App struct {
	Router *mux.Router
	DB     *sql.DB
}

func (a *App) Initialize(user, password, dbname string) {
	connectionString := fmt.Sprintf("%s:%s@/%s", user, password, dbname)
	var err error
	a.DB, err = sql.Open("mysql", connectionString)
	if err != nil {
		log.Fatal(err)
	}
	a.Router = mux.NewRouter()
	a.initializeRoutes()
}

func (a *App) initializeRoutes() {
	a.Router.HandleFunc("/users", a.getUsers).Methods("GET")
	a.Router.HandleFunc("/user", a.createUser).Methods("POST")
	a.Router.HandleFunc("/user/{id:[0-9]+}", a.getUser).Methods("GET")
	a.Router.HandleFunc("/user/{id:[0-9]+}", a.updateUser).Methods("PUT")
	a.Router.HandleFunc("/user/{id:[0-9]+}", a.deleteUser).Methods("DELETE")
}

func (a *App) Run(addr string) {
	log.Fatal(http.ListenAndServe(addr, a.Router))
}

// menampilkan user dengan single row
func (a *App) getUser(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "ID User salah")
		return
	}
	u := user{ID: id}
	if err := u.getUser(a.DB); err != nil {
		switch err {
		case sql.ErrNoRows:
			respondWithError(w, http.StatusNotFound, "User tidak ditemukan")
		default:
			respondWithError(w, http.StatusInternalServerError, err.Error())
		}
		return
	}
	respondWithJSON(w, http.StatusOK, u)
}

// menampilkan user dengan multiple rows
func (a *App) getUsers(w http.ResponseWriter, r *http.Request) {
	count, _ := strconv.Atoi(r.FormValue("count"))
	start, _ := strconv.Atoi(r.FormValue("start"))
	if count > 10 || count < 1 {
		count = 10
	}
	if start < 0 {
		start = 0
	}
	users, err := getUsers(a.DB, start, count)
	if err != nil {
		respondWithError(w, http.StatusInternalServerError, err.Error())
		return
	}
	respondWithJSON(w, http.StatusOK, users)
}

// menyimpan data user baru
func (a *App) createUser(w http.ResponseWriter, r *http.Request) {
	var u user
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&u); err != nil {
		respondWithError(w, http.StatusBadRequest, "request error")
		return
	}
	defer r.Body.Close()
	if err := u.createUser(a.DB); err != nil {
		respondWithError(w, http.StatusInternalServerError, err.Error())
		return
	}
	respondWithJSON(w, http.StatusCreated, u)
}

// mengubah data user
func (a *App) updateUser(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "ID User salah")
		return
	}
	var u user
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&u); err != nil {
		respondWithError(w, http.StatusBadRequest, "request tidak valid")
		return
	}
	defer r.Body.Close()
	u.ID = id
	if err := u.updateUser(a.DB); err != nil {
		respondWithError(w, http.StatusInternalServerError, err.Error())
		return
	}
	respondWithJSON(w, http.StatusOK, u)
}

// menghapus data user
func (a *App) deleteUser(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "ID User salah")
		return
	}
	u := user{ID: id}
	if err := u.deleteUser(a.DB); err != nil {
		respondWithError(w, http.StatusInternalServerError, err.Error())
		return
	}
	respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"})
}

// mengeksekusi http request
func respondWithError(w http.ResponseWriter, code int, message string) {
	respondWithJSON(w, code, map[string]string{"error": message})
}

// menampilkan response dari http request
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
	response, _ := json.Marshal(payload)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(response)
}

Ok, tahap development sudah selesai. Sekarang kita akan mulai mengetes API dengan Golang yang sudah kita buat.

Testing API Golang

Sebelum kita mulai melakukan testing pada API Golang, kita harus mengcompile kode yang sudah kita buat menjadi sebuah file yang dapat di eksekusi.

Untuk dapat mengcompile kode program Go kita dapat menggunakan perintah berikut :

go build

Perintah diatas akan menghasilkan sebuah file yang nantinya kita eksekusi untuk menjalankan API dengan Golang. Pada kasus disini file yang muncul di laptop saya adalah api-go karena berada di dalam direktori api-go.

Kita dapat mengeksekusi file tersebut dengan menggunakan perintah ./api-go pada console. Perintah tersebut akan menjalankan service Go pada alamat http://127.0.0.1:8080.

Berikut contoh untuk mengirimkan request ke API Golang, saya menggunakan Insomnia sebagai Rest Client untuk menguji API.

api dengan golang - fetch user
api dengan golang fetch user

Sekian dulu tutorial dari saya tentang cara membuat API dengan Golang dan MySQL. Kamu bisa mendownload kode program dari artikel ini pada repositori Github saya.

Selamat mencoba dan semoga bermanfaat.


El Cicko

Nama saya Riky Fahri Hasibuan, Saya yang biasa nulis di blog ini. Blog ini adalah sarana dokumentasi dari apa yang saya kerjakan dan tidak ada salahnya juga saya sebarkan. Jika artikel di blog bermanfaat, kamu bisa memberikan apresiasi pada blog ini dengan memberikan donasi pada blog ini.

1 Komentar

firman · Desember 20, 2019 pada 8:02 am

bikin table pake ‘user’ tapi query insert ke ‘users’
typo huruf S 😀

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *