Go หรือ Golang เป็นภาษาที่เหมาะกับงาน Backend, REST API และ Microservices เพราะมีจุดเด่นด้านประสิทธิภาพ, การทำงานแบบ Concurrency, การคอมไพล์เป็นไฟล์ไบนารี และมี Standard Library ที่แข็งแรง โดยเฉพาะแพ็กเกจ net/http

บทความนี้จะพาเขียน REST API แบบ CRUD ด้วย Go + Gin ตั้งแต่เริ่มสร้างโปรเจกต์ ไปจนถึงทดสอบด้วย curl และรันผ่าน Docker

ตัวอย่างนี้ใช้แนวทางที่เหมาะกับ Go รุ่นปัจจุบัน โดย ณ วันที่ 24 กันยายน 2026 Go มีรุ่นเสถียรในสาย 1.27 และ Go 1.27.1 ออกเมื่อ 1 กันยายน 2026


#1. REST API คืออะไร

REST API เป็นรูปแบบการออกแบบ API ที่นิยมใช้ HTTP Method เพื่อจัดการ Resource เช่น

HTTP Method Endpoint ความหมาย
GET /api/v1/products อ่านรายการสินค้า
GET /api/v1/products/1 อ่านสินค้าตาม ID
POST /api/v1/products เพิ่มสินค้า
PUT /api/v1/products/1 แก้ไขสินค้า
DELETE /api/v1/products/1 ลบสินค้า

ข้อมูลจะรับส่งในรูปแบบ JSON

{
  "id": 1,
  "name": "Mechanical Keyboard",
  "price": 2590,
  "stock": 10
}

#2. ทำไมเลือก Go สำหรับพัฒนา API

จุดเด่นของ Go ได้แก่

  • Syntax ค่อนข้างเรียบง่าย
  • Compile เป็น Native Binary
  • เริ่มทำงานเร็วและใช้ทรัพยากรค่อนข้างมีประสิทธิภาพ
  • มี Goroutine สำหรับงาน Concurrent
  • มี net/http ใน Standard Library
  • เหมาะกับ REST API, Microservices, Cloud Native และระบบ Distributed
  • ทำ Docker Image ขนาดเล็กได้ด้วย Multi-stage Build

สำหรับบทความนี้จะใช้ Gin Framework เพื่อช่วยเรื่อง Routing, JSON Binding, Validation และ Middleware


#3. เตรียมเครื่องมือ

ตรวจสอบ Go

go version

ตัวอย่าง

go version go1.27.1 darwin/arm64

ถ้ายังไม่มี Go สามารถติดตั้งจาก

https://go.dev/dl/

เครื่องมือที่แนะนำ

  • Go
  • VS Code หรือ GoLand
  • VS Code Go Extension
  • curl หรือ Postman
  • Docker Desktop ถ้าต้องการทดลอง Container

#4. สร้างโปรเจกต์

สร้างโฟลเดอร์

mkdir go-product-api
cd go-product-api

สร้าง Go Module

go mod init example.com/go-product-api

ติดตั้ง Gin

go get github.com/gin-gonic/gin

จัด dependency

go mod tidy

โครงสร้างเริ่มต้น

go-product-api/
├── go.mod
├── go.sum
└── main.go

#5. สร้าง Product Model

สร้างไฟล์ main.go

package main

import (
	"net/http"
	"strconv"
	"sync"

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

type Product struct {
	ID    int64   `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
	Stock int     `json:"stock"`
}

type ProductInput struct {
	Name  string  `json:"name" binding:"required"`
	Price float64 `json:"price" binding:"required,gt=0"`
	Stock int     `json:"stock" binding:"gte=0"`
}

Product ใช้สำหรับ Response ส่วน ProductInput ใช้สำหรับข้อมูลที่ Client ส่งเข้ามา

ตัวอย่าง Validation

Name  string  `json:"name" binding:"required"`
Price float64 `json:"price" binding:"required,gt=0"`
Stock int     `json:"stock" binding:"gte=0"`

หมายถึง

  • name ห้ามว่าง
  • price ต้องมากกว่า 0
  • stock ต้องมีค่าอย่างน้อย 0

#6. สร้าง In-memory Data Store

เพื่อให้เห็นหลักการ REST API ก่อน ตัวอย่างนี้ยังไม่เชื่อม Database

เพิ่มโค้ด

var (
	products = []Product{
		{ID: 1, Name: "Mechanical Keyboard", Price: 2590, Stock: 10},
		{ID: 2, Name: "Wireless Mouse", Price: 1290, Stock: 25},
	}

	nextID int64 = 3
	mu     sync.RWMutex
)

sync.RWMutex ช่วยป้องกันปัญหาเมื่อหลาย Request เข้ามาอ่าน/เขียนข้อมูลพร้อมกัน

ในระบบจริงควรใช้ Database เช่น PostgreSQL, MySQL หรือ MongoDB แทน In-memory Store


#7. สร้าง Health Check API

func healthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":  "ok",
		"service": "go-product-api",
	})
}

Endpoint

GET /health

Response

{
  "service": "go-product-api",
  "status": "ok"
}

#8. GET รายการสินค้าทั้งหมด

func getProducts(c *gin.Context) {
	mu.RLock()
	defer mu.RUnlock()

	c.JSON(http.StatusOK, gin.H{
		"data": products,
	})
}

Endpoint

GET /api/v1/products

#9. GET สินค้าตาม ID

func getProductByID(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	mu.RLock()
	defer mu.RUnlock()

	for _, product := range products {
		if product.ID == id {
			c.JSON(http.StatusOK, gin.H{
				"data": product,
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

Endpoint

GET /api/v1/products/:id

เช่น

GET /api/v1/products/1

#10. POST เพิ่มสินค้า

func createProduct(c *gin.Context) {
	var input ProductInput

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	product := Product{
		ID:    nextID,
		Name:  input.Name,
		Price: input.Price,
		Stock: input.Stock,
	}

	nextID++
	products = append(products, product)

	c.JSON(http.StatusCreated, gin.H{
		"data": product,
	})
}

Request

POST /api/v1/products
Content-Type: application/json

Body

{
  "name": "USB-C Hub",
  "price": 1490,
  "stock": 15
}

#11. PUT แก้ไขสินค้า

func updateProduct(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	var input ProductInput

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	for i := range products {
		if products[i].ID == id {
			products[i].Name = input.Name
			products[i].Price = input.Price
			products[i].Stock = input.Stock

			c.JSON(http.StatusOK, gin.H{
				"data": products[i],
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

Request

{
  "name": "USB-C Hub Pro",
  "price": 1890,
  "stock": 20
}

#12. DELETE สินค้า

func deleteProduct(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	for i, product := range products {
		if product.ID == id {
			products = append(products[:i], products[i+1:]...)

			c.JSON(http.StatusOK, gin.H{
				"message": "product deleted",
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

#13. กำหนด Router

สร้างฟังก์ชัน main()

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

	router.GET("/health", healthCheck)

	api := router.Group("/api/v1")
	{
		api.GET("/products", getProducts)
		api.GET("/products/:id", getProductByID)
		api.POST("/products", createProduct)
		api.PUT("/products/:id", updateProduct)
		api.DELETE("/products/:id", deleteProduct)
	}

	if err := router.Run(":8080"); err != nil {
		panic(err)
	}
}

#14. Source Code ฉบับสมบูรณ์

ไฟล์ main.go

package main

import (
	"net/http"
	"strconv"
	"sync"

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

type Product struct {
	ID    int64   `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
	Stock int     `json:"stock"`
}

type ProductInput struct {
	Name  string  `json:"name" binding:"required"`
	Price float64 `json:"price" binding:"required,gt=0"`
	Stock int     `json:"stock" binding:"gte=0"`
}

var (
	products = []Product{
		{ID: 1, Name: "Mechanical Keyboard", Price: 2590, Stock: 10},
		{ID: 2, Name: "Wireless Mouse", Price: 1290, Stock: 25},
	}

	nextID int64 = 3
	mu     sync.RWMutex
)

func healthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":  "ok",
		"service": "go-product-api",
	})
}

func getProducts(c *gin.Context) {
	mu.RLock()
	defer mu.RUnlock()

	c.JSON(http.StatusOK, gin.H{
		"data": products,
	})
}

func getProductByID(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	mu.RLock()
	defer mu.RUnlock()

	for _, product := range products {
		if product.ID == id {
			c.JSON(http.StatusOK, gin.H{
				"data": product,
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

func createProduct(c *gin.Context) {
	var input ProductInput

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	product := Product{
		ID:    nextID,
		Name:  input.Name,
		Price: input.Price,
		Stock: input.Stock,
	}

	nextID++
	products = append(products, product)

	c.JSON(http.StatusCreated, gin.H{
		"data": product,
	})
}

func updateProduct(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	var input ProductInput

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	for i := range products {
		if products[i].ID == id {
			products[i].Name = input.Name
			products[i].Price = input.Price
			products[i].Stock = input.Stock

			c.JSON(http.StatusOK, gin.H{
				"data": products[i],
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

func deleteProduct(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "invalid product id",
		})
		return
	}

	mu.Lock()
	defer mu.Unlock()

	for i, product := range products {
		if product.ID == id {
			products = append(products[:i], products[i+1:]...)

			c.JSON(http.StatusOK, gin.H{
				"message": "product deleted",
			})
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{
		"error": "product not found",
	})
}

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

	router.GET("/health", healthCheck)

	api := router.Group("/api/v1")
	{
		api.GET("/products", getProducts)
		api.GET("/products/:id", getProductByID)
		api.POST("/products", createProduct)
		api.PUT("/products/:id", updateProduct)
		api.DELETE("/products/:id", deleteProduct)
	}

	if err := router.Run(":8080"); err != nil {
		panic(err)
	}
}

#15. Run API

จัด dependency

go mod tidy

รัน

go run .

Server จะทำงานที่

http://localhost:8080

#16. ทดสอบด้วย curl

#Health Check

curl http://localhost:8080/health

#GET Products

curl http://localhost:8080/api/v1/products

#GET Product by ID

curl http://localhost:8080/api/v1/products/1

#POST Product

curl -X POST http://localhost:8080/api/v1/products \
  -H "Content-Type: application/json" \
  -d '{
    "name": "USB-C Hub",
    "price": 1490,
    "stock": 15
  }'

#PUT Product

curl -X PUT http://localhost:8080/api/v1/products/1 \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mechanical Keyboard Pro",
    "price": 3290,
    "stock": 8
  }'

#DELETE Product

curl -X DELETE http://localhost:8080/api/v1/products/1

#17. HTTP Status Code ที่ควรใช้

Status ความหมาย
200 OK Request สำเร็จ
201 Created สร้าง Resource สำเร็จ
400 Bad Request ข้อมูล Request ไม่ถูกต้อง
401 Unauthorized ยังไม่ได้ Authentication
403 Forbidden ไม่มีสิทธิ์เข้าถึง
404 Not Found ไม่พบ Resource
409 Conflict ข้อมูลขัดแย้ง
422 Unprocessable Content Validation ไม่ผ่าน
500 Internal Server Error Server Error

#18. เพิ่ม Dockerfile

สร้างไฟล์ Dockerfile

FROM golang:1.27-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -o server .

FROM alpine:latest

WORKDIR /app

COPY --from=builder /app/server /app/server

EXPOSE 8080

CMD ["/app/server"]

Build Image

docker build -t go-product-api .

Run Container

docker run --rm -p 8080:8080 go-product-api

ทดสอบ

curl http://localhost:8080/health

#19. Docker Compose

สร้าง compose.yaml

services:
  api:
    build: .
    container_name: go-product-api
    ports:
      - "8080:8080"

รัน

docker compose up --build

หยุด

docker compose down

#20. Architecture ของตัวอย่าง

flowchart LR
    C[Client / Frontend / Postman] -->|HTTP Request| R[Gin Router]
    R --> H[Handler]
    H --> V[Validation]
    V --> S[In-memory Store]
    S --> H
    H -->|JSON Response| C

เมื่อพัฒนาเป็น Production สามารถขยายเป็น

flowchart LR
    C[Web / Mobile Client] --> G[API Gateway]
    G --> A[Go REST API]
    A --> DB[(PostgreSQL)]
    A --> R[(Redis)]
    A --> O[Observability]
    O --> P[Prometheus]
    O --> L[Logs]

#21. โครงสร้างโปรเจกต์สำหรับระบบจริง

เมื่อระบบเริ่มใหญ่ขึ้น ไม่ควรรวมทุกอย่างไว้ใน main.go

ตัวอย่าง

go-product-api/
├── cmd/
│   └── api/
│       └── main.go
├── internal/
│   ├── handler/
│   │   └── product_handler.go
│   ├── service/
│   │   └── product_service.go
│   ├── repository/
│   │   └── product_repository.go
│   ├── model/
│   │   └── product.go
│   └── middleware/
├── migrations/
├── go.mod
├── go.sum
├── Dockerfile
└── compose.yaml

แนวคิดการแยก Layer

HTTP Request
     |
     v
 Handler
     |
     v
 Service
     |
     v
 Repository
     |
     v
 Database

#Handler

รับผิดชอบ

  • HTTP Request
  • Path Parameter
  • Query Parameter
  • JSON Binding
  • HTTP Response

#Service

รับผิดชอบ

  • Business Logic
  • Validation ระดับธุรกิจ
  • Transaction Flow

#Repository

รับผิดชอบ

  • ติดต่อ Database
  • Query
  • Insert
  • Update
  • Delete

#22. สิ่งที่ควรเพิ่มเมื่อทำ Production API

ระบบจริงควรเพิ่มองค์ประกอบต่อไปนี้

  1. PostgreSQL/MySQL
  2. Migration
  3. Environment Variable
  4. Authentication เช่น JWT/OAuth 2.0
  5. Authorization / RBAC
  6. Structured Logging
  7. Request ID
  8. Rate Limiting
  9. CORS
  10. API Versioning
  11. OpenAPI/Swagger
  12. Unit Test
  13. Integration Test
  14. CI/CD
  15. Metrics และ Tracing
  16. Graceful Shutdown
  17. Secret Management

#23. Roadmap การเรียนรู้ Go API

Go Fundamentals
      |
      v
net/http
      |
      v
Gin / Echo / Fiber / Chi
      |
      v
REST API + JSON
      |
      v
PostgreSQL
      |
      v
Repository + Service Layer
      |
      v
JWT Authentication
      |
      v
Testing
      |
      v
Docker
      |
      v
CI/CD
      |
      v
Kubernetes / Cloud

ถ้าเพิ่งเริ่ม แนะนำลำดับดังนี้

  1. สร้าง CRUD API แบบ In-memory
  2. เชื่อม PostgreSQL
  3. เพิ่ม JWT
  4. แยก Handler / Service / Repository
  5. เขียน Unit Test และ Integration Test
  6. เพิ่ม Swagger/OpenAPI
  7. Dockerize
  8. Deploy ขึ้น Cloud หรือ Kubernetes

#สรุป

Go เหมาะกับการพัฒนา REST API เพราะมี Runtime ที่เบา, ประสิทธิภาพดี, รองรับ Concurrency และมี Ecosystem สำหรับ Backend ที่ครบถ้วน

Workshop นี้เริ่มจาก Go + Gin + In-memory Store เพื่อทำความเข้าใจแกนสำคัญของ API ได้แก่

Route
  -> Handler
      -> Validation
          -> Data
              -> JSON Response

เมื่อเข้าใจพื้นฐานแล้ว สามารถพัฒนาต่อไปเป็นระบบ Production ด้วย PostgreSQL, JWT, Swagger, Testing, Docker, CI/CD และ Kubernetes ได้ไม่ยาก


#แหล่งอ้างอิง