📚 Series: Belajar Golang dari Nol sampai Deploy
Kenapa Gin?
Gin adalah HTTP framework Go yang paling populer. Keunggulannya:
- ⚡ Sangat cepat — routing berbasis radix tree
- 📦 Ringan — minimal overhead
- 🔧 Middleware yang mudah
- 📄 Binding JSON/form otomatis
- 💬 Dokumentasi lengkap dan komunitas besar
Setup Project
mkdir todo-api && cd todo-api
go mod init github.com/username/todo-api
# Install Gin
go get github.com/gin-gonic/gin
Struktur Project
todo-api/
├── main.go
├── handlers/
│ └── task.go
├── models/
│ └── task.go
├── go.mod
└── go.sum
Model Task
Buat file models/task.go:
package models
import "time"
type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
type CreateTaskInput struct {
Title string `json:"title" binding:"required,min=1,max=200"`
}
type UpdateTaskInput struct {
Title *string `json:"title" binding:"omitempty,min=1,max=200"`
Completed *bool `json:"completed"`
}
Handler
Buat file handlers/task.go:
package handlers
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/username/todo-api/models"
)
// Simulasi database dengan slice (sementara, sebelum pakai GORM di Part 7)
var tasks = []models.Task{}
var nextID = 1
func GetTasks(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"data": tasks,
"total": len(tasks),
})
}
func GetTask(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID tidak valid"})
return
}
for _, task := range tasks {
if task.ID == id {
c.JSON(http.StatusOK, gin.H{"data": task})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
}
func CreateTask(c *gin.Context) {
var input models.CreateTaskInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
task := models.Task{
ID: nextID,
Title: input.Title,
Completed: false,
CreatedAt: time.Now(),
}
nextID++
tasks = append(tasks, task)
c.JSON(http.StatusCreated, gin.H{
"message": "Task berhasil dibuat",
"data": task,
})
}
func UpdateTask(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID tidak valid"})
return
}
var input models.UpdateTaskInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
for i, task := range tasks {
if task.ID == id {
if input.Title != nil {
tasks[i].Title = *input.Title
}
if input.Completed != nil {
tasks[i].Completed = *input.Completed
}
c.JSON(http.StatusOK, gin.H{"data": tasks[i]})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
}
func DeleteTask(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID tidak valid"})
return
}
for i, task := range tasks {
if task.ID == id {
tasks = append(tasks[:i], tasks[i+1:]...)
c.JSON(http.StatusOK, gin.H{"message": "Task berhasil dihapus"})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
}
Main — Setup Router
Edit main.go:
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/username/todo-api/handlers"
)
func main() {
r := gin.Default()
// Middleware CORS sederhana
r.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// Health check
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// API v1
v1 := r.Group("/api/v1")
{
tasks := v1.Group("/tasks")
tasks.GET("", handlers.GetTasks)
tasks.GET("/:id", handlers.GetTask)
tasks.POST("", handlers.CreateTask)
tasks.PATCH("/:id", handlers.UpdateTask)
tasks.DELETE("/:id", handlers.DeleteTask)
}
log.Println("Server berjalan di http://localhost:8080")
r.Run(":8080")
}
Jalankan dan Test
go run main.go
# Test dengan curl:
# Buat task baru
curl -X POST http://localhost:8080/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Belajar Go Part 6"}'
# Lihat semua task
curl http://localhost:8080/api/v1/tasks
# Update task (mark as done)
curl -X PATCH http://localhost:8080/api/v1/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
# Hapus task
curl -X DELETE http://localhost:8080/api/v1/tasks/1
Middleware Logging
// Custom middleware untuk log request
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next() // proses request
duration := time.Since(start)
log.Printf("[%d] %s %s — %v",
c.Writer.Status(),
c.Request.Method,
c.Request.URL.Path,
duration,
)
}
}
// Pakai di main.go
r.Use(Logger())
Komentar 0