Programming 25 Aug 2026 11 views 0 komentar

Belajar Go untuk Pemula - Membangun REST API dengan Gin dan MySQL

Belajar Go untuk Pemula - Membangun REST API dengan Gin dan MySQL

Beberapa bulan lalu, saya masih skeptis sama Go. Semua project backend yang saya kerjakan pakai PHP dan Python, dan rasanya sudah nyaman banget. Go? Buat saya itu bahasa buat orang yang suka ribet - harus compile, harus mikirin binary, dan ekosistemnya kayaknya masih kecil. Ternyata, dugaan saya salah total. Begitu saya beneran duduk dan bikin project kecil dengan Go, dalam satu minggu saya paham kenapa banyak developer backend di luar sana pindah ke bahasa ini.

Go (atau Golang) itu bahasa pemrograman yang dibuat Google pada 2009, dirancang oleh Robert Griesemer, Rob Pike, dan Ken Thompson. Tujuan utamanya simpel: bahasa yang cepat, aman, dan enak dipakai untuk membangun server dan tool skalabel. Di artikel ini, saya ajak kamu belajar Go dari nol sambil praktek langsung bikin REST API pakai framework Gin dan MySQL. Kita bahas dari instalasi sampai testing endpoint dengan curl, jadi setelah baca ini kamu punya project nyata yang bisa kamu kembangkan sendiri.

Kenapa Saya Pindah ke Go

Alasan pertama: kecepatan. Go itu compiled language, jadi binary hasil compile-nya jalan jauh lebih cepat daripada script Python atau PHP yang harus diinterpretasi tiap request. Untuk API yang saya kelola, latensi turun drastis tanpa perlu ubah arsitektur.

Alasan kedua: simplicity. Go sengaja dibuat minimalis. Nggak ada class inheritance, nggak ada magic yang tersembunyi. Yang kamu lihat di kode, ya itu yang jalan. Buat saya yang udah bertahun-tahun begelut dengan framework PHP yang kadang terlalu magical, ini sangat menyegarkan.

Alasan ketiga: deployment. Hasil compile Go berupa satu binary statis. Nggak perlu install runtime, nggak perlu composer install atau pip install di server. Upload binary, jalankan, selesai. Ini alasan kenapa Go populer banget buat microservice dan tool infrastructure - Docker, Kubernetes, Terraform, semuanya ditulis dengan Go.

Instalasi Go di Linux

Cara paling gampang di Ubuntu atau Debian pakai apt, tapi versinya sering ketinggalan. Saya saranin download dari situs resmi go.dev supaya dapat versi terbaru:


# Download Go 1.22 (cek versi terbaru di https://go.dev/dl/)
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz

# Ekstrak ke /usr/local
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz

# Tambahkan ke PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc

# Cek versi
go version

Kalau kamu di Windows, tinggal download installer .msi dan ikuti wizard-nya. Di macOS, bisa pakai brew install go. Setelah terinstall, cek dengan go version. Go juga akan memakai folder ~/go sebagai GOPATH untuk menyimpan module dan binary hasil build.

Struktur Project

Sebelum nulis kode, siapkan dulu folder project-nya. Untuk project Go modern, kita pakai Go Modules - ini standar dependency management sejak Go 1.11:


mkdir go-rest-api
cd go-rest-api
go mod init go-rest-api

Struktur project-nya nanti seperti ini:


go-rest-api/
|-- go.mod
|-- go.sum
|-- main.go
|-- models/
|   `-- user.go
`-- handlers/
    `-- user_handler.go

Jangan panik lihat folder models dan handlers - kita buat satu per satu. Untuk project pemula, struktur sesimpel ini sudah cukup. Nggak perlu over-engineering dengan clean architecture di tutorial pertama.

Hello World HTTP Server

Go punya package net/http di standard library. Jadi untuk bikin server HTTP, kamu nggak perlu framework dulu:


package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello, Go!</h1>")
    })

    fmt.Println("Server jalan di :8080")
    http.ListenAndServe(":8080", nil)
}

Jalankan dengan go run main.go, lalu buka http://localhost:8080. Server HTTP pertama kamu sudah jalan! Perhatikan betapa sedikit kode yang dibutuhkan - nggak ada boilerplate, nggak ada config file, langsung jalan.

Kenalan dengan Gin Framework

Standard library Go udah bagus, tapi untuk REST API production kita butuh routing yang lebih rapi, middleware, dan JSON binding. Di sinilah Gin masuk. Gin adalah web framework paling populer di ekosistem Go - dipakai banyak perusahaan karena performanya tinggi dan API-nya enak dipakai.


go get github.com/gin-gonic/gin

Contoh minimal Gin:


package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

Jalankan, lalu test dengan curl http://localhost:8080/ping. Kamu akan dapat respons JSON {"message":"pong"}. Dari sini kita bangun API yang beneran.

Koneksi ke MySQL

Sebagian besar aplikasi butuh database. Untuk MySQL di Go, kita pakai driver resmi dari komunitas: github.com/go-sql-driver/mysql, plus package database/sql dari standard library.

Pertama, siapkan database-nya:


CREATE DATABASE IF NOT EXISTS go_blog;
USE go_blog;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Lalu buat file models/user.go untuk koneksi dan query:


package models

import (
    "database/sql"

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

var db *sql.DB

func InitDB(dsn string) error {
    var err error
    db, err = sql.Open("mysql", dsn)
    if err != nil {
        return err
    }
    return db.Ping()
}

type User struct {
    ID    int64  `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func GetAllUsers() ([]User, error) {
    rows, err := db.Query("SELECT id, name, email FROM users ORDER BY id DESC")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var users []User
    for rows.Next() {
        var u User
        if err := rows.Scan(&u.ID, &u.Name, &u.Email); err != nil {
            return nil, err
        }
        users = append(users, u)
    }
    return users, nil
}

DSN (Data Source Name) untuk MySQL formatnya: user:password@tcp(host:port)/dbname. Contoh: root:rahasia@tcp(127.0.0.1:3306)/go_blog.

Membuat CRUD Lengkap

Sekarang kita gabungkan semuanya. Buat handlers/user_handler.go berisi handler GET, POST, PUT, dan DELETE:


package handlers

import (
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
    "go-rest-api/models"
)

func GetUsers(c *gin.Context) {
    users, err := models.GetAllUsers()
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, users)
}

func CreateUser(c *gin.Context) {
    var input struct {
        Name  string `json:"name" binding:"required"`
        Email string `json:"email" binding:"required,email"`
    }
    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    id, err := models.InsertUser(input.Name, input.Email)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusCreated, gin.H{"id": id})
}

func UpdateUser(c *gin.Context) {
    id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
    var input struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }
    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    if err := models.UpdateUser(id, input.Name, input.Email); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"message": "user updated"})
}

func DeleteUser(c *gin.Context) {
    id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
    if err := models.DeleteUser(id); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"message": "user deleted"})
}

Lalu update main.go untuk mendaftarkan routes:


package main

import (
    "github.com/gin-gonic/gin"
    "go-rest-api/handlers"
    "go-rest-api/models"
)

func main() {
    // Ganti dengan kredensial MySQL kamu
    if err := models.InitDB("root:rahasia@tcp(127.0.0.1:3306)/go_blog"); err != nil {
        panic(err)
    }

    r := gin.Default()

    r.GET("/users", handlers.GetUsers)
    r.POST("/users", handlers.CreateUser)
    r.PUT("/users/:id", handlers.UpdateUser)
    r.DELETE("/users/:id", handlers.DeleteUser)

    r.Run(":8080")
}

Jangan lupa tambahkan fungsi InsertUser, UpdateUser, dan DeleteUser di models/user.go:


func InsertUser(name, email string) (int64, error) {
    result, err := db.Exec(
        "INSERT INTO users (name, email) VALUES (?, ?)",
        name, email,
    )
    if err != nil {
        return 0, err
    }
    return result.LastInsertId()
}

func UpdateUser(id int64, name, email string) error {
    _, err := db.Exec(
        "UPDATE users SET name = ?, email = ? WHERE id = ?",
        name, email, id,
    )
    return err
}

func DeleteUser(id int64) error {
    _, err := db.Exec("DELETE FROM users WHERE id = ?", id)
    return err
}

Perhatikan penggunaan placeholder ? - ini mencegah SQL injection. Jangan pernah menggabungkan input user langsung ke string query.

Testing API dengan curl

Setelah semua file siap, jalankan go run main.go, lalu test endpoint satu per satu:


# Ambil semua user (awalnya kosong)
curl http://localhost:8080/users

# Buat user baru
curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Budi","email":"[email protected]"}'

# Update user id 1
curl -X PUT http://localhost:8080/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Budi Santoso"}'

# Hapus user id 1
curl -X DELETE http://localhost:8080/users/1

# Cek lagi
curl http://localhost:8080/users

Kalau semua berjalan, kamu baru saja membangun REST API lengkap - create, read, update, delete - dengan Go, Gin, dan MySQL. Nggak sampai 200 baris kode.

Middleware dan Logging di Gin

Satu hal yang sering dilupakan pemula: logging. Waktu aplikasi jalan di production, kamu butuh tahu request apa yang masuk, berapa lama diproses, dan error apa yang terjadi. Kabar baiknya, gin.Default() sudah otomatis memasang dua middleware penting: Logger dan Recovery. Logger mencatat setiap request ke console, Recovery mencegah server crash total kalau ada panic di handler.

Kalau mau tambah middleware sendiri, caranya gampang. Contoh middleware sederhana untuk menghitung lama request:


func RequestTimer() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        duration := time.Since(start)
        log.Printf("%s %s took %v", c.Request.Method, c.Request.URL.Path, duration)
    }
}

Daftarkan dengan r.Use(RequestTimer()) setelah router dibuat. Middleware ini jalan untuk semua route, jadi cocok buat hal-hal kayak autentikasi, CORS, atau rate limiting. Nanti kalau API kamu makin besar, kamu bakal appreciate banget punya middleware yang rapi.

Kebiasaan Baik yang Langsung Saya Terapkan

  • Selalu handle error. Di Go, error itu value biasa, bukan exception. Biasakan cek err setelah setiap operasi yang bisa gagal. Ini yang bikin kode Go terasa jujur.
  • Pakai gofmt. Jalankan gofmt -w . sebelum commit. Format kode Go itu standar, nggak ada debat soal spasi atau indentasi antar developer.
  • Simpan konfigurasi di environment variable. Jangan hardcode DSN database di kode. Pakai os.Getenv("DB_DSN") supaya gampang ganti antara local dan production.
  • Manfaatkan goroutine dengan hati-hati. Goroutine itu murah dan powerful, tapi untuk pemula, fokus dulu ke kode yang benar sebelum mikirin concurrency.
  • Selalu defer rows.Close(). Kalau kamu query database, pastikan rows ditutup setelah selesai dipakai. Kalau lupa, koneksi database bakal bocor dan lama-lama server kehabisan koneksi.
  • Gunakan context untuk timeout. Di handler, pakai c.Request.Context() biar query database bisa dibatalkan kalau client putus koneksi. Ini mencegah goroutine menggantung di background.

Error yang Sering Muncul (dan Solusinya)

1. "command not found: go" - PATH belum ke-set. Cek /usr/local/go/bin sudah masuk ~/.bashrc, lalu jalankan source ~/.bashrc.

2. "dial tcp 127.0.0.1:3306: connect: connection refused" - MySQL belum jalan atau port-nya beda. Cek dengan systemctl status mysql dan pastikan DSN pakai port yang benar.

3. "unexpected newline" saat parse JSON di curl - Kemungkinan besar karena backslash di command. Di shell, pastikan string JSON-nya satu baris atau pakai tanda kutip yang benar.

4. "undefined: gin" - Module belum ke-download. Jalankan go mod tidy untuk menyinkronkan go.mod dengan import di kode.

5. Port 8080 sudah dipakai - Ganti port di r.Run(":8081") atau matikan proses lain yang memakai port tersebut.

6. "Error 1146: Table doesn't exist" - Nama tabel atau database salah. Pastikan kamu sudah menjalankan script CREATE TABLE di database go_blog, bukan di database lain.

7. JSON response kosong atau null - Biasanya karena struct field tidak punya json tag, atau data memang kosong di database. Tambahkan json tag di struct dan cek isi tabel dengan SELECT * FROM users.

Go vs PHP vs Python untuk Backend

Setelah beberapa bulan pakai Go, ini kesimpulan saya soal kapan harus pilih bahasa mana:

  • Performance. Go menang telak. Compiled binary tanpa runtime overhead. Kalau API kamu harus handle ribuan request per detik, Go pilihan aman.
  • Learning curve. PHP dan Python lebih ramah pemula. Tapi Go nggak jauh lebih susah - syntax-nya kecil, dokumentasinya jelas, dan error message-nya membantu banget.
  • Deployment. Go paling gampang. Satu binary, langsung jalan. PHP butuh web server plus PHP-FPM, Python butuh venv plus process manager kayak gunicorn atau uvicorn.
  • Ekosistem. Python menang untuk data science dan AI. PHP menang untuk CMS kayak WordPress. Go menang untuk cloud-native tooling dan microservice.
  • Concurrency. Goroutine bikin handling concurrent request jadi jauh lebih sederhana dibanding thread manual di Python atau PHP.

Intinya: kalau project kamu CRUD biasa dengan traffic sedang, PHP atau Python tetap fine. Tapi kalau kamu bangun API yang bakal dipakai banyak client, microservice, atau tool yang harus cepat dan ringan, Go pilihan yang sangat masuk akal.

Deploy API ke VPS

Bagian paling menyenangkan dari Go: deploy. Nggak ada dependency yang harus diinstall di server. Cukup build di lokal atau di CI, upload binary, dan jalankan.


# Build binary (di lokal atau server)
GOOS=linux GOARCH=amd64 go build -o myapp .

# Upload ke server (contoh pakai scp)
scp myapp user@vps-ip:/opt/go-rest-api/

# Jalankan
cd /opt/go-rest-api
./myapp

Supaya lebih proper, daftarkan sebagai service systemd biar auto-restart kalau crash dan ikut start saat server reboot:


# /etc/systemd/system/go-rest-api.service
[Unit]
Description=Go REST API
After=network.target

[Service]
User=www-data
WorkingDirectory=/opt/go-rest-api
ExecStart=/opt/go-rest-api/myapp
Restart=always
Environment=DB_DSN=root:rahasia@tcp(127.0.0.1:3306)/go_blog

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl enable --now go-rest-api
sudo systemctl status go-rest-api

Lalu pasang nginx sebagai reverse proxy supaya bisa pakai domain dan SSL gratis dari Let's Encrypt. Pola ini sama kayak deploy aplikasi backend lain, cuma bedanya nggak perlu setup PHP-FPM atau gunicorn - binary Go langsung listen di port-nya.

Kesimpulan

Go itu bahasa yang pas buat developer yang mau backend cepat, simpel, dan gampang di-deploy. Dengan Gin dan MySQL, kamu bisa membangun REST API production-ready dalam waktu singkat. Yang saya suka: semua tooling-nya built-in, dokumentasinya rapi, dan komunitasnya aktif banget.

Kalau kamu baru mulai, jangan langsung lompat ke microservice atau clean architecture. Bikin dulu API sederhana seperti di tutorial ini, deploy ke VPS, dan rasakan sendiri bedanya. Setelah itu baru eksplor goroutine, testing dengan httptest, dan deployment dengan Docker.

Kalau kamu serius mau dalemin Go, roadmap selanjutnya yang saya saranin: tambahkan JWT authentication, tulis unit test dengan package testing bawaan, bikin Dockerfile multi-stage biar image-nya kecil, dan pelajari context untuk cancelation request. Semua itu natural next step dari project yang barusan kita buat.

Kamu sendiri gimana? Udah pernah coba Go, atau masih ragu-ragu mau pindah dari PHP, Python, atau Node.js? Cerita di kolom komentar ya - atau kalau ada bagian tutorial ini yang kurang jelas, tanya aja. Saya bantu semampunya.


Bagikan artikel ini:

Komentar (0)

Belum ada komentar. Jadilah yang pertama memberikan tanggapan!

Tinggalkan Komentar