Skip to content
← All Skills
🍸

Gin

Go web framework — high-performance HTTP server, middleware, RESTful API

What I Can Do

  • สร้าง RESTful API ด้วย Gin ตั้งแต่ routing ถึง production-ready
  • ออกแบบ middleware chain สำหรับ auth, logging, rate limiting, CORS
  • Implement request validation ด้วย struct tags + binding
  • จัดการ error handling แบบ centralized
  • เขียน integration tests สำหรับ HTTP handlers

Commands I Use Daily

bash
# install Gin
go get -u github.com/gin-gonic/gin

# run server ตอน dev
go run ./cmd/server

# run tests สำหรับ handler layer
go test -v ./internal/handler/...

# build binary
CGO_ENABLED=0 go build -o main ./cmd/server

Router & Route Groups

Gin ใช้ httprouter-based routing — เร็วกว่า default net/http เพราะใช้ radix tree, r.GET("/users/:id", handler) สำหรับ path parameters, r.Group("/api/v1") จัด routes เป็นกลุ่มพร้อม shared middleware

HTTP Methods & RESTful Routing

r.GET, r.POST, r.PUT, r.PATCH, r.DELETE — map กับ REST operations (CRUD), ใช้ path parameters (:id) และ wildcard (*path) สำหรับ flexible routing, r.Any match ทุก methods

Context (gin.Context)

*gin.Context เป็น core ของ Gin — ใช้ read request (params, query, body, headers), write response (JSON, HTML, file), pass data ระหว่าง middleware (c.Set/c.Get), control flow (c.Next, c.Abort)

Request Binding & Validation

Bind request body/query/params เข้า struct ด้วย c.ShouldBindJSON, c.ShouldBindQuery — ใช้ struct tags binding:"required,min=1,max=100" validate อัตโนมัติ, return error ถ้า validation fail

Middleware

Middleware คือ function ที่ run ก่อน/หลัง handler — r.Use(middleware) apply ทั้ง router, group.Use() apply เฉพาะ group, c.Next() ไป handler ถัดไป, c.Abort() หยุด chain

Built-in Middleware

  • gin.Logger() — log ทุก request (method, path, status, latency)
  • gin.Recovery() — recover จาก panic, return 500 แทนที่จะ crash
  • ใช้ทั้งคู่เป็น default: r := gin.Default() = gin.New() + Logger + Recovery

Custom Middleware

เขียน middleware เอง — pattern: func MyMiddleware() gin.HandlerFunc { return func(c *gin.Context) { ... c.Next() ... } } ใช้สำหรับ auth, rate limiting, request ID, timing, CORS

Response Formats

  • c.JSON(200, gin.H{"key": "value"}) — JSON response (ใช้บ่อยสุด)
  • c.String, c.HTML, c.XML, c.File, c.Stream
  • gin.H เป็น shortcut สำหรับ map[string]any
  • Set headers ด้วย c.Header("key", "value")

Error Handling

  • c.Error(err) — collect errors ใน context, handle รวมใน error middleware
  • Custom error types + centralized error handler middleware
  • c.AbortWithStatusJSON(400, gin.H{"error": msg}) — return error ทันที

Path Parameters & Query Strings

  • Path paramsr.GET("/users/:id"), อ่านด้วย c.Param("id")
  • Query strings/users?page=1&limit=10, อ่านด้วย c.Query("page"), c.DefaultQuery("limit", "20")
  • ใช้ c.ShouldBindQuery(&struct) bind query params เข้า struct

Authentication Middleware

Implement JWT/OAuth middleware — extract token จาก Authorization header, validate, set user info ใน context ด้วย c.Set("userID", id) handler อ่านด้วย c.Get("userID")

Rate Limiting

ใช้ middleware + token bucket หรือ sliding window algorithm — จำกัด requests per IP/user/endpoint, return 429 Too Many Requests เมื่อเกิน limit, ใช้ Redis เก็บ counters สำหรับ distributed systems

File Upload & Static Files

  • c.FormFile("file") — receive uploaded file
  • c.SaveUploadedFile(file, dst) — save to disk
  • r.Static("/assets", "./public") — serve static files
  • r.MaxMultipartMemory — limit upload size

Graceful Shutdown

ใช้ http.Server แทน r.Run() เพื่อ handle OS signals — srv.Shutdown(ctx) drain existing connections, ปิด cleanly ไม่ drop in-flight requests

Testing Handlers

ใช้ httptest.NewRecorder() + gin.CreateTestContext() — สร้าง request, เรียก handler, assert response status/body โดยไม่ต้อง start server จริง

Structured Logging

Replace default Logger middleware ด้วย structured logger (zerolog, zap) — log request ID, user ID, latency, errors เป็น JSON สำหรับ centralized log aggregation

CORS Configuration

ใช้ github.com/gin-contrib/cors middleware — กำหนด allowed origins, methods, headers สำหรับ cross-origin requests จาก frontend, config ต่าง environment (dev allow all, prod restrict)

Dependency Injection in Handlers

ส่ง dependencies (DB, cache, services) เข้า handler ผ่าน struct method หรือ closure — type Handler struct { db *sql.DB } แล้ว r.GET("/users", h.ListUsers) ทำให้ test ง่าย mock dependencies ได้

Performance Tuning

  • gin.SetMode(gin.ReleaseMode) — ปิด debug logging ใน production
  • Object pooling ด้วย sync.Pool สำหรับ frequently allocated objects
  • Response compression ด้วย gzip middleware
  • Connection pooling สำหรับ downstream services

API Versioning

จัดการ API versions ด้วย route groups — v1 := r.Group("/api/v1"), v2 := r.Group("/api/v2") แต่ละ version มี handlers แยก, share middleware ที่ common, deprecate versions เก่าอย่าง graceful

ปัญหาที่เจอบ่อย & วิธีแก้

Binding errors ไม่แสดง validation message ที่เข้าใจง่าย

ShouldBindJSON return error เป็น raw validator errors ที่อ่านยาก

สาเหตุ: go-playground/validator return validator.ValidationErrors ที่เป็น struct ซับซ้อน — ส่งกลับ client ตรงๆ ได้ข้อความแบบ Key: 'User.Name' Error:Field validation for 'Name' failed on the 'required' tag

วิธีแก้:

  • เขียน error translator ที่แปลง validator.ValidationErrors เป็น map ของ field → readable message
  • สร้าง middleware ที่ format validation errors ก่อนส่ง response เป็น {"errors": {"name": "ต้องกรอก", "email": "format ไม่ถูก"}}
  • Register custom validation messages ด้วย validator.RegisterTranslation

Middleware execution order ทำงานไม่ตรงที่คิด

Code หลัง c.Next() ใน middleware แรก กลับ run ทีหลังสุด

สาเหตุ: c.Next() ทำงานแบบ stack (LIFO) — middleware A เรียก c.Next() → เข้า middleware B → เข้า handler → กลับมา code หลัง c.Next() ของ B → กลับมา code หลัง c.Next() ของ A

วิธีแก้:

  • วาด execution flow ก่อน implement — code ก่อน c.Next() = pre-handler, code หลัง c.Next() = post-handler
  • ใช้ logging middleware debug ลำดับ: log ก่อนและหลัง c.Next() ดู sequence
  • ถ้าต้องการ middleware ที่ run หลัง response (เช่น logging latency) ให้วาง code หลัง c.Next()

Context value type assertion panic

c.Get("key") return interface{} — assert ผิด type จะ panic

สาเหตุ: c.Get() return (value interface{}, exists bool) ถ้าไม่ตรวจ exists แล้ว assert type ตรงๆ เช่น c.Get("userID").(int) จะ panic ถ้า key ไม่มีหรือ type ไม่ตรง

วิธีแก้:

  • ใช้ comma-ok pattern: val, exists := c.Get("userID"); if !exists { ... }
  • ใช้ helper methods: c.GetString("key"), c.GetInt("key") ที่ return zero value ถ้าไม่มี
  • สร้าง typed helper functions สำหรับ values ที่ใช้บ่อย เช่น func GetUserID(c *gin.Context) (int, error)

Goroutine ใช้ gin.Context หลัง request จบ — data race

ส่ง *gin.Context เข้า goroutine แล้วเจอ data race หรือ panic

สาเหตุ: gin.Context ถูก reuse ผ่าน sync.Pool — หลัง request จบ context จะถูก reset และใช้กับ request ใหม่ goroutine ที่ยังถือ reference เดิมจะอ่าน/เขียน data ของ request อื่น

วิธีแก้:

  • ใช้ c.Copy() ก่อนส่งเข้า goroutine เสมอ: cCopy := c.Copy(); go func() { /* ใช้ cCopy */ }()
  • หรือ copy เฉพาะ values ที่ต้องการ: userID := c.GetInt("userID"); go func() { /* ใช้ userID */ }()
  • อย่าเรียก c.JSON() หรือ write response จาก goroutine — response ต้อง write ใน handler เท่านั้น

CORS preflight request ไม่ผ่าน

Frontend เรียก API แล้วเจอ CORS error ใน browser

สาเหตุ: Browser ส่ง OPTIONS preflight request ก่อน actual request — ถ้า CORS middleware อยู่หลัง auth middleware, preflight จะถูก reject เพราะไม่มี token

วิธีแก้:

  • วาง CORS middleware ก่อน auth middleware เสมอ: r.Use(cors.Default()) ก่อน r.Use(authMiddleware())
  • ใช้ github.com/gin-contrib/cors config ให้ครบ: AllowOrigins, AllowMethods, AllowHeaders
  • ตรวจ browser DevTools → Network tab → ดู preflight request และ response headers

Route conflicts — :id ชนกับ static path

/users/:id กับ /users/admin conflict กัน

สาเหตุ: Gin ใช้ radix tree routing — :id เป็น wildcard ที่ match ทุก string รวมถึง "admin" ทำให้ register ทั้งสอง routes ไม่ได้ หรือ route ผิดตัว

วิธีแก้:

  • restructure URL: ใช้ /admin/users แทน /users/admin หลีกเลี่ยง conflict กับ /users/:id
  • ใช้ route groups แยก patterns ให้ชัดเจน
  • ถ้าจำเป็นต้องใช้ path เดียวกัน: handle ใน handler เดียวแล้วตรวจ param ว่าเป็น ID หรือ keyword

Related Skills

  • Go — ภาษาที่ Gin สร้างขึ้นมา
  • GORM — ORM ที่ใช้คู่กับ Gin เป็น data layer
  • Fiber — alternative Go web framework ที่เร็วกว่า
  • Docker — containerize Gin services สำหรับ deployment