- 1. REST API คืออะไร
- 2. ทำไมเลือก GoFiber
- Workshop: Product REST API
- 6. สร้าง Product Model
- 7. GET: ดึงข้อมูลสินค้าทั้งหมด
- 8. GET: ดึงข้อมูลสินค้าตาม ID
- 9. POST: เพิ่มสินค้า
- 10. PUT: แก้ไขสินค้า
- 11. DELETE: ลบสินค้า
- 12. Route Group
- 13. Middleware
- 14. Code ฉบับสมบูรณ์
- 15. รัน API
- 16. ทดสอบ API ด้วย curl
- 17. HTTP Status Code ที่ควรรู้
- 18. โครงสร้าง Response
- 19. แยกโครงสร้างโปรเจกต์
- 20. Handler Layer
- 21. Service Layer
- 22. Repository Layer
- 23. ต่อ PostgreSQL
- 24. Authentication ด้วย JWT
- 25. Environment Variables
- 26. Dockerize GoFiber
- 27. Docker Compose
- 28. Testing
- 29. แนวทาง API สำหรับ Production
- 30. REST API Architecture ที่เหมาะกับระบบจริง
- 31. Roadmap การเรียนรู้ต่อ
- 32. สรุป
- References

การพัฒนา Backend ในปัจจุบันมักต้องสร้าง REST API เพื่อให้ Frontend, Mobile Application หรือระบบอื่นสามารถเรียกใช้ข้อมูลและบริการจาก Server ได้ผ่าน HTTP
ภาษา Go (Golang) เป็นตัวเลือกที่ได้รับความนิยมสำหรับงาน Backend เพราะมีจุดเด่นด้านประสิทธิภาพ การใช้ทรัพยากรที่ค่อนข้างต่ำ การรองรับ Concurrent Programming และสามารถ Build เป็นไฟล์ Binary เพื่อนำไป Deploy ได้สะดวก
หนึ่งใน Web Framework ที่ได้รับความนิยมใน Go คือ Fiber ซึ่งมีรูปแบบการเขียน API ที่กระชับและได้รับแรงบันดาลใจจาก Express.js
บทความนี้จะสร้าง REST API แบบ CRUD ด้วย GoFiber v3 ตั้งแต่เริ่มต้นจนสามารถนำไปต่อยอดกับ PostgreSQL, JWT, Docker และ CI/CD ได้
บทความนี้อ้างอิง Fiber v3 ปัจจุบัน โดยเอกสารทางการระบุว่าต้องใช้ Go 1.25 ขึ้นไป และติดตั้ง Fiber ผ่านโมดูล
github.com/gofiber/fiber/v3
#1. REST API คืออะไร
REST หรือ Representational State Transfer เป็นแนวทางออกแบบ Web API โดยใช้ HTTP Method เพื่อกำหนดการดำเนินการกับ Resource
ตัวอย่าง Resource ชื่อ products
| HTTP Method | Endpoint | การทำงาน |
|---|---|---|
GET |
/api/v1/products |
แสดงสินค้าทั้งหมด |
GET |
/api/v1/products/:id |
แสดงสินค้าตาม ID |
POST |
/api/v1/products |
เพิ่มสินค้า |
PUT |
/api/v1/products/:id |
แก้ไขสินค้า |
DELETE |
/api/v1/products/:id |
ลบสินค้า |
แนวคิดพื้นฐานสามารถมองได้ว่า
Client
|
| HTTP Request
v
REST API
|
+--> Router
|
+--> Handler
|
+--> Business Logic
|
+--> Repository / Database
|
v
JSON Response
#2. ทำไมเลือก GoFiber
Fiber เป็น Web Framework สำหรับ Go ที่สร้างอยู่บน fasthttp และออกแบบ API ให้ใช้งานง่ายในลักษณะที่นักพัฒนาที่เคยใช้ Express.js สามารถเรียนรู้ได้รวดเร็ว
จุดเด่นที่เหมาะกับการสร้าง REST API ได้แก่
- Routing เขียนง่าย
- รองรับ Middleware
- จัดการ JSON Request/Response ได้สะดวก
- รองรับ Route Group
- มี Middleware สำหรับ Logger, CORS, Recover, Limiter และอื่น ๆ
- เหมาะกับ Microservices และ Container
- สามารถ Build เป็น Single Binary
- ทำงานร่วมกับ PostgreSQL, MySQL, Redis และระบบ Cloud Native ได้ง่าย
#Workshop: Product REST API
ใน Workshop นี้เราจะสร้าง API สำหรับจัดการสินค้า โดยเริ่มจากการเก็บข้อมูลแบบ In-memory ก่อน เพื่อให้เข้าใจ Flow ของ Fiber ได้ง่าย
โครงสร้างข้อมูล Product
{
"id": 1,
"name": "Mechanical Keyboard",
"price": 2590
}
#3. ตรวจสอบ Go
ตรวจสอบว่าเครื่องติดตั้ง Go แล้ว
go version
Fiber v3 ระบุให้ใช้ Go 1.25 ขึ้นไป
หากใช้ Go เวอร์ชันเก่าควรอัปเดตก่อนเริ่มโปรเจกต์
#4. สร้างโปรเจกต์
สร้าง Folder
mkdir go-fiber-rest-api
cd go-fiber-rest-api
สร้าง Go Module
go mod init example.com/go-fiber-rest-api
ติดตั้ง Fiber v3
go get github.com/gofiber/fiber/v3
ตรวจสอบไฟล์
go-fiber-rest-api/
├── go.mod
└── go.sum
#5. Hello World ด้วย GoFiber
สร้างไฟล์
main.go
เพิ่ม Code
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Hello GoFiber v3",
})
})
log.Fatal(app.Listen(":3000"))
}
รัน
go run .
เปิด Browser หรือเรียก API
curl http://localhost:3000
ผลลัพธ์
{
"message": "Hello GoFiber v3"
}
#6. สร้าง Product Model
ในตัวอย่างแรกจะเขียนทั้งหมดไว้ใน main.go เพื่อให้เห็น Flow ได้ชัดเจน
กำหนด Product
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
และ Request สำหรับสร้างหรือแก้ไขข้อมูล
type ProductRequest struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
สร้างข้อมูลเริ่มต้น
var products = []Product{
{
ID: 1,
Name: "Mechanical Keyboard",
Price: 2590,
},
{
ID: 2,
Name: "Gaming Mouse",
Price: 1290,
},
}
เพื่อให้ตัวอย่างปลอดภัยขึ้นเมื่อต้องรับ Request หลายรายการพร้อมกัน เราจะใช้ sync.RWMutex
var mu sync.RWMutex
#7. GET: ดึงข้อมูลสินค้าทั้งหมด
สร้าง Endpoint
GET /api/v1/products
Handler
func getProducts(c fiber.Ctx) error {
mu.RLock()
defer mu.RUnlock()
return c.JSON(fiber.Map{
"data": products,
})
}
ผลลัพธ์ตัวอย่าง
{
"data": [
{
"id": 1,
"name": "Mechanical Keyboard",
"price": 2590
},
{
"id": 2,
"name": "Gaming Mouse",
"price": 1290
}
]
}
#8. GET: ดึงข้อมูลสินค้าตาม ID
Endpoint
GET /api/v1/products/:id
ตัวอย่าง
GET /api/v1/products/1
Handler
func getProductByID(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
mu.RLock()
defer mu.RUnlock()
for _, product := range products {
if product.ID == id {
return c.JSON(fiber.Map{
"data": product,
})
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
#9. POST: เพิ่มสินค้า
Endpoint
POST /api/v1/products
Request Body
{
"name": "USB-C Hub",
"price": 1490
}
Fiber v3 ใช้ Binding API ผ่าน
c.Bind().Body(...)
Handler
func createProduct(c fiber.Ctx) error {
var req ProductRequest
if err := c.Bind().Body(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid request body",
})
}
if req.Name == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "name is required",
})
}
if req.Price < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "price must be greater than or equal to 0",
})
}
mu.Lock()
defer mu.Unlock()
nextID := 1
if len(products) > 0 {
nextID = products[len(products)-1].ID + 1
}
product := Product{
ID: nextID,
Name: req.Name,
Price: req.Price,
}
products = append(products, product)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"data": product,
})
}
เมื่อสร้างสำเร็จควรตอบ
201 Created
#10. PUT: แก้ไขสินค้า
Endpoint
PUT /api/v1/products/:id
ตัวอย่าง Request
{
"name": "USB-C Hub 10-in-1",
"price": 1890
}
Handler
func updateProduct(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
var req ProductRequest
if err := c.Bind().Body(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid request body",
})
}
if req.Name == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "name is required",
})
}
if req.Price < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "price must be greater than or equal to 0",
})
}
mu.Lock()
defer mu.Unlock()
for i := range products {
if products[i].ID == id {
products[i].Name = req.Name
products[i].Price = req.Price
return c.JSON(fiber.Map{
"data": products[i],
})
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
#11. DELETE: ลบสินค้า
Endpoint
DELETE /api/v1/products/:id
Handler
func deleteProduct(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
mu.Lock()
defer mu.Unlock()
for i, product := range products {
if product.ID == id {
products = append(products[:i], products[i+1:]...)
return c.SendStatus(fiber.StatusNoContent)
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
เมื่อสำเร็จ API ตอบ
204 No Content
#12. Route Group
แทนที่จะเขียน
app.Get("/api/v1/products", ...)
app.Get("/api/v1/products/:id", ...)
app.Post("/api/v1/products", ...)
สามารถสร้าง Route Group
api := app.Group("/api")
v1 := api.Group("/v1")
productRoutes := v1.Group("/products")
productRoutes.Get("/", getProducts)
productRoutes.Get("/:id", getProductByID)
productRoutes.Post("/", createProduct)
productRoutes.Put("/:id", updateProduct)
productRoutes.Delete("/:id", deleteProduct)
ช่วยให้รองรับ API Versioning ได้ง่าย เช่น
/api/v1/products
/api/v2/products
#13. Middleware
Middleware คือ Function ที่ทำงานระหว่าง Request และ Handler
ตัวอย่าง Flow
Request
|
v
Logger
|
v
CORS
|
v
Recover
|
v
Router
|
v
Handler
|
v
Response
ติดตั้งผ่าน Package ของ Fiber v3 ได้โดยตรง
import (
"github.com/gofiber/fiber/v3/middleware/cors"
"github.com/gofiber/fiber/v3/middleware/logger"
recoverer "github.com/gofiber/fiber/v3/middleware/recover"
)
ใช้งาน
app.Use(logger.New())
app.Use(recoverer.New())
app.Use(cors.New())
#Logger
ใช้บันทึก HTTP Request/Response
app.Use(logger.New())
#Recover
ป้องกัน Panic ไม่ให้ Application Process จบลงจาก Handler โดยตรง และส่ง Error ไปยัง Error Handler ของ Fiber
app.Use(recoverer.New())
#CORS
ใช้ควบคุม Cross-Origin Request โดยเฉพาะเมื่อ Frontend และ Backend อยู่คนละ Origin
สำหรับ Production ไม่ควรปล่อยค่า CORS กว้างเกินความจำเป็น ควรกำหนด Origin ที่อนุญาตให้ชัดเจน
#14. Code ฉบับสมบูรณ์
ไฟล์ main.go
package main
import (
"log"
"strconv"
"sync"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
"github.com/gofiber/fiber/v3/middleware/logger"
recoverer "github.com/gofiber/fiber/v3/middleware/recover"
)
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
type ProductRequest struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
var (
products = []Product{
{
ID: 1,
Name: "Mechanical Keyboard",
Price: 2590,
},
{
ID: 2,
Name: "Gaming Mouse",
Price: 1290,
},
}
mu sync.RWMutex
)
func main() {
app := fiber.New()
app.Use(logger.New())
app.Use(recoverer.New())
app.Use(cors.New())
app.Get("/", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "GoFiber REST API",
})
})
app.Get("/health", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"status": "ok",
})
})
api := app.Group("/api")
v1 := api.Group("/v1")
productRoutes := v1.Group("/products")
productRoutes.Get("/", getProducts)
productRoutes.Get("/:id", getProductByID)
productRoutes.Post("/", createProduct)
productRoutes.Put("/:id", updateProduct)
productRoutes.Delete("/:id", deleteProduct)
log.Fatal(app.Listen(":3000"))
}
func getProducts(c fiber.Ctx) error {
mu.RLock()
defer mu.RUnlock()
return c.JSON(fiber.Map{
"data": products,
})
}
func getProductByID(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
mu.RLock()
defer mu.RUnlock()
for _, product := range products {
if product.ID == id {
return c.JSON(fiber.Map{
"data": product,
})
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
func createProduct(c fiber.Ctx) error {
var req ProductRequest
if err := c.Bind().Body(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid request body",
})
}
if req.Name == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "name is required",
})
}
if req.Price < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "price must be greater than or equal to 0",
})
}
mu.Lock()
defer mu.Unlock()
nextID := 1
if len(products) > 0 {
nextID = products[len(products)-1].ID + 1
}
product := Product{
ID: nextID,
Name: req.Name,
Price: req.Price,
}
products = append(products, product)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"data": product,
})
}
func updateProduct(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
var req ProductRequest
if err := c.Bind().Body(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid request body",
})
}
if req.Name == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "name is required",
})
}
if req.Price < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "price must be greater than or equal to 0",
})
}
mu.Lock()
defer mu.Unlock()
for i := range products {
if products[i].ID == id {
products[i].Name = req.Name
products[i].Price = req.Price
return c.JSON(fiber.Map{
"data": products[i],
})
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
func deleteProduct(c fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid product id",
})
}
mu.Lock()
defer mu.Unlock()
for i, product := range products {
if product.ID == id {
products = append(products[:i], products[i+1:]...)
return c.SendStatus(fiber.StatusNoContent)
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "product not found",
})
}
#15. รัน API
จัด Format Code
go fmt ./...
ตรวจสอบ Dependency
go mod tidy
รัน
go run .
Server ทำงานที่
http://localhost:3000
#16. ทดสอบ API ด้วย curl
#Health Check
curl http://localhost:3000/health
#GET Products
curl http://localhost:3000/api/v1/products
#GET Product By ID
curl http://localhost:3000/api/v1/products/1
#POST Product
curl -X POST http://localhost:3000/api/v1/products \
-H "Content-Type: application/json" \
-d '{
"name": "USB-C Hub",
"price": 1490
}'
#PUT Product
curl -X PUT http://localhost:3000/api/v1/products/1 \
-H "Content-Type: application/json" \
-d '{
"name": "Mechanical Keyboard Pro",
"price": 3290
}'
#DELETE Product
curl -i -X DELETE \
http://localhost:3000/api/v1/products/1
#17. HTTP Status Code ที่ควรรู้
| Status | ความหมาย | ตัวอย่าง |
|---|---|---|
200 OK |
Request สำเร็จ | GET / PUT |
201 Created |
สร้าง Resource สำเร็จ | POST |
204 No Content |
สำเร็จและไม่มี Response Body | DELETE |
400 Bad Request |
Request ไม่ถูกต้อง | JSON หรือ ID ไม่ถูกต้อง |
401 Unauthorized |
ยังไม่ได้ Authentication | JWT ไม่ถูกต้อง |
403 Forbidden |
ไม่มีสิทธิ์ | User ไม่มี Role |
404 Not Found |
ไม่พบ Resource | Product ไม่มี |
409 Conflict |
ข้อมูลขัดแย้ง | Duplicate Data |
422 Unprocessable Content |
Validation ไม่ผ่าน | Field ไม่ถูกต้อง |
500 Internal Server Error |
Server Error | Database Error |
#18. โครงสร้าง Response
ควรกำหนดรูปแบบ Response ให้สม่ำเสมอ
ตัวอย่าง Success
{
"data": {
"id": 1,
"name": "Mechanical Keyboard",
"price": 2590
}
}
ตัวอย่าง Error
{
"error": "product not found"
}
ในระบบขนาดใหญ่สามารถเพิ่ม
{
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "product not found"
}
}
เพื่อให้ Frontend จัดการ Error ได้ง่ายขึ้น
#19. แยกโครงสร้างโปรเจกต์
เมื่อ Application ใหญ่ขึ้น ไม่ควรเก็บทุกอย่างไว้ใน main.go
ตัวอย่างโครงสร้าง
go-fiber-rest-api/
│
├── cmd/
│ └── api/
│ └── main.go
│
├── internal/
│ ├── handler/
│ │ └── product_handler.go
│ │
│ ├── service/
│ │ └── product_service.go
│ │
│ ├── repository/
│ │ └── product_repository.go
│ │
│ ├── model/
│ │ └── product.go
│ │
│ ├── middleware/
│ └── routes/
│ └── routes.go
│
├── go.mod
├── go.sum
├── Dockerfile
└── compose.yaml
Flow
HTTP Request
|
v
Router
|
v
Handler
|
v
Service
|
v
Repository
|
v
Database
ข้อดีของการแบ่ง Layer คือ
- Code อ่านง่าย
- Unit Test ง่าย
- เปลี่ยน Database ได้สะดวก
- Business Logic ไม่ผูกกับ HTTP Handler
- รองรับ Dependency Injection ได้ดีขึ้น
- เหมาะสำหรับระบบขนาดกลางและใหญ่
#20. Handler Layer
หน้าที่ของ Handler ควรเน้นเรื่อง HTTP เช่น
- รับ Request
- Bind JSON
- ตรวจ Parameter เบื้องต้น
- เรียก Service
- กำหนด HTTP Status
- ส่ง JSON Response
ตัวอย่าง
type ProductHandler struct {
service ProductService
}
func (h *ProductHandler) Create(c fiber.Ctx) error {
var req ProductRequest
if err := c.Bind().Body(&req); err != nil {
return fiber.ErrBadRequest
}
product, err := h.service.Create(req)
if err != nil {
return err
}
return c.Status(fiber.StatusCreated).JSON(product)
}
#21. Service Layer
Service ใช้เก็บ Business Logic
type ProductService interface {
Create(req ProductRequest) (*Product, error)
GetAll() ([]Product, error)
GetByID(id int) (*Product, error)
Update(id int, req ProductRequest) (*Product, error)
Delete(id int) error
}
Service ไม่ควรสนใจรายละเอียดว่า Request มาจาก Fiber หรือ HTTP Framework ใด
#22. Repository Layer
Repository ใช้จัดการ Data Access
type ProductRepository interface {
Create(product *Product) error
FindAll() ([]Product, error)
FindByID(id int) (*Product, error)
Update(product *Product) error
Delete(id int) error
}
Repository สามารถ Implement ด้วย
- PostgreSQL
- MySQL
- MongoDB
- SQLite
- Redis
- In-memory
ได้โดยไม่ต้องเปลี่ยน Business Logic มาก
#23. ต่อ PostgreSQL
Production API ควรเก็บข้อมูลใน Database จริง
Architecture
Client
|
v
GoFiber
|
v
Handler
|
v
Service
|
v
Repository
|
v
PostgreSQL
Library ที่สามารถใช้ได้ เช่น
database/sql
pgx
GORM
sqlc
ถ้าต้องการควบคุม SQL ชัดเจน pgx หรือ sqlc เป็นตัวเลือกที่น่าสนใจ
ถ้าต้องการ ORM และพัฒนา CRUD ได้รวดเร็ว สามารถใช้ GORM
#24. Authentication ด้วย JWT
เมื่อ API ต้องมี User Login สามารถเพิ่ม JWT
Flow ตัวอย่าง
POST /auth/login
|
v
ตรวจ Username / Password
|
v
สร้าง JWT
|
v
Client เก็บ Token
|
v
Authorization: Bearer <token>
|
v
JWT Middleware
|
v
Protected API
Endpoint เช่น
POST /api/v1/auth/login
GET /api/v1/profile
GET /api/v1/products
POST /api/v1/products
#25. Environment Variables
ไม่ควร Hard-code Configuration เช่น
password := "mypassword"
ควรใช้ Environment Variables
ตัวอย่าง .env
APP_PORT=3000
DB_HOST=postgres
DB_PORT=5432
DB_NAME=shop
DB_USER=app
DB_PASSWORD=change-me
JWT_SECRET=change-me
และต้องไม่ Commit .env ที่มี Secret ขึ้น Git Repository
#26. Dockerize GoFiber
ตัวอย่าง Multi-stage Dockerfile
FROM golang:1.25-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:3.22
WORKDIR /app
COPY --from=builder /app/server ./server
EXPOSE 3000
CMD ["./server"]
Build
docker build -t go-fiber-api .
Run
docker run --rm \
-p 3000:3000 \
go-fiber-api
#27. Docker Compose
หากมี PostgreSQL สามารถใช้ Compose
services:
api:
build: .
ports:
- "3000:3000"
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: shop
DB_USER: app
DB_PASSWORD: secret
depends_on:
- postgres
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: shop
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Run
docker compose up --build
#28. Testing
REST API ควรมี Automated Test
สามารถทดสอบในระดับต่าง ๆ ได้ เช่น
Unit Test
|
v
Service Test
|
v
Handler Test
|
v
Integration Test
|
v
API / End-to-End Test
เครื่องมือที่ใช้ร่วมได้
- Go
testing httptest- Postman
- Newman
- Postman CLI
- Playwright API Testing
- k6 สำหรับ Load Testing
- JMeter สำหรับ Performance Testing
#29. แนวทาง API สำหรับ Production
ระบบจริงควรเพิ่มเติมอย่างน้อย
#Validation
ตรวจสอบข้อมูลก่อนบันทึก
name != ""
price >= 0
#Authentication
เช่น JWT หรือ OAuth 2.0
#Authorization
ตรวจ Role หรือ Permission
Admin
Staff
Customer
#Structured Logging
เก็บข้อมูลที่ช่วย Trace Request เช่น
request_id
method
path
status
latency
#Rate Limiting
ช่วยควบคุมจำนวน Request
#CORS Policy
กำหนด Origin ที่อนุญาตอย่างชัดเจน
#Timeout
กำหนด Timeout สำหรับ Database หรือ External Service
#Graceful Shutdown
หยุด Server อย่างถูกต้องเมื่อ Deploy หรือ Restart Container
#Observability
เพิ่ม
Metrics
Logs
Traces
แล้วเชื่อมกับระบบ เช่น
Prometheus
Grafana
OpenTelemetry
Loki
#30. REST API Architecture ที่เหมาะกับระบบจริง
แนวทางหนึ่งที่สามารถใช้ได้
┌───────────────┐
│ Client │
└───────┬───────┘
│
▼
┌─────────────────┐
│ GoFiber │
│ Router │
└────────┬────────┘
│
┌──────────────▼──────────────┐
│ Middleware │
│ Logger / CORS / JWT / Rate │
└──────────────┬──────────────┘
│
▼
┌───────────────┐
│ Handler │
└───────┬───────┘
│
▼
┌───────────────┐
│ Service │
└───────┬───────┘
│
▼
┌───────────────┐
│ Repository │
└───────┬───────┘
│
▼
┌───────────────┐
│ PostgreSQL │
└───────────────┘
#31. Roadmap การเรียนรู้ต่อ
หลังจากสร้าง CRUD API สำเร็จ สามารถพัฒนาต่อเป็นลำดับดังนี้
Go Fundamentals
↓
GoFiber
↓
REST API
↓
CRUD
↓
Validation
↓
PostgreSQL
↓
Repository Pattern
↓
JWT Authentication
↓
Unit / Integration Testing
↓
Docker
↓
CI/CD
↓
Kubernetes
↓
Observability
#32. สรุป
GoFiber ช่วยให้การพัฒนา REST API ด้วย Go ทำได้รวดเร็วและ Code มีรูปแบบที่กระชับ โดยเฉพาะสำหรับผู้ที่คุ้นเคยกับ Express.js
Workshop นี้ครอบคลุมพื้นฐานสำคัญ ได้แก่
Routing
CRUD
JSON Binding
Validation
HTTP Status Code
Middleware
Route Group
Error Response
Project Structure
Docker
สำหรับระบบ Production ควรต่อยอดไปสู่ Architecture แบบแยก Layer
Handler
↓
Service
↓
Repository
↓
Database
จากนั้นเพิ่มองค์ประกอบสำคัญ เช่น
PostgreSQL
JWT
Validation
Testing
Docker
CI/CD
Kubernetes
Observability
แนวทางนี้ทำให้ REST API ที่เริ่มต้นจากโปรเจกต์เล็กสามารถขยายไปเป็น Backend Service หรือ Microservice ที่ดูแลรักษาได้ง่ายขึ้นในอนาคต
#References
- Fiber Documentation: https://docs.gofiber.io/
- Fiber CRUD API Guide: https://docs.gofiber.io/blog/build-a-crud-app-with-fiber/
- What's New in Fiber v3: https://docs.gofiber.io/blog/whats-new-in-fiber-v3/
- Fiber GitHub Releases: https://github.com/gofiber/fiber/releases
- Go Documentation: https://go.dev/doc/