- 1. REST API คืออะไร
- 2. ทำไมเลือก Go สำหรับพัฒนา API
- 3. เตรียมเครื่องมือ
- 4. สร้างโปรเจกต์
- 5. สร้าง Product Model
- 6. สร้าง In-memory Data Store
- 7. สร้าง Health Check API
- 8. GET รายการสินค้าทั้งหมด
- 9. GET สินค้าตาม ID
- 10. POST เพิ่มสินค้า
- 11. PUT แก้ไขสินค้า
- 12. DELETE สินค้า
- 13. กำหนด Router
- 14. Source Code ฉบับสมบูรณ์
- 15. Run API
- 16. ทดสอบด้วย curl
- 17. HTTP Status Code ที่ควรใช้
- 18. เพิ่ม Dockerfile
- 19. Docker Compose
- 20. Architecture ของตัวอย่าง
- 21. โครงสร้างโปรเจกต์สำหรับระบบจริง
- 22. สิ่งที่ควรเพิ่มเมื่อทำ Production API
- 23. Roadmap การเรียนรู้ Go API
- สรุป
- แหล่งอ้างอิง
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ต้องมากกว่า 0stockต้องมีค่าอย่างน้อย 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
ระบบจริงควรเพิ่มองค์ประกอบต่อไปนี้
- PostgreSQL/MySQL
- Migration
- Environment Variable
- Authentication เช่น JWT/OAuth 2.0
- Authorization / RBAC
- Structured Logging
- Request ID
- Rate Limiting
- CORS
- API Versioning
- OpenAPI/Swagger
- Unit Test
- Integration Test
- CI/CD
- Metrics และ Tracing
- Graceful Shutdown
- 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
ถ้าเพิ่งเริ่ม แนะนำลำดับดังนี้
- สร้าง CRUD API แบบ In-memory
- เชื่อม PostgreSQL
- เพิ่ม JWT
- แยก Handler / Service / Repository
- เขียน Unit Test และ Integration Test
- เพิ่ม Swagger/OpenAPI
- Dockerize
- 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 ได้ไม่ยาก
#แหล่งอ้างอิง
- Go Documentation: https://go.dev/doc/
- Go Tutorials: https://go.dev/doc/tutorial/
- RESTful API with Go and Gin: https://go.dev/doc/tutorial/web-service-gin
- Go Release History: https://go.dev/doc/devel/release
- Gin Web Framework: https://gin-gonic.com/