Fiber
Express-inspired Go web framework — zero allocation routing, high throughput, fasthttp-based
What I Can Do
- สร้าง high-performance API ด้วย Fiber + fasthttp
- Migrate จาก Express.js/Gin มา Fiber ได้ราบรื่น (API คล้าย Express)
- Implement WebSocket, Server-Sent Events สำหรับ real-time features
- ออกแบบ middleware chain สำหรับ production workloads
- Optimize throughput สำหรับ high-traffic endpoints
Commands I Use Daily
# install Fiber v2
go get github.com/gofiber/fiber/v2
# run server ตอน dev
go run ./cmd/server
# run tests
go test -v ./internal/handler/...
# benchmark (ใช้ hey หรือ wrk)
hey -n 10000 -c 100 http://localhost:3000/api/healthApp & Routing Basics
app := fiber.New() สร้าง app instance — routing syntax คล้าย Express: app.Get("/users/:id", handler), app.Post("/users", handler) ใช้ path parameters (:id), optional params (:id?), wildcards (*)
Route Groups & Prefixes
api := app.Group("/api"), v1 := api.Group("/v1") — จัด routes เป็น hierarchy, apply middleware per group, แยก concerns ระหว่าง public/private/admin routes
Context (fiber.Ctx)
*fiber.Ctx คือ core — อ่าน request ด้วย c.Params("id"), c.Query("page"), c.Body(), เขียน response ด้วย c.JSON(), c.SendString(), c.Status(201).JSON() ต่างจาก Gin ตรงที่ Ctx ไม่ reuse ข้าม requests (pool-based)
Request Parsing & Body Parser
c.BodyParser(&struct)— parse JSON/XML/form body เข้า structc.Params("id")— path parametersc.Query("page", "1")— query string with default valuec.Get("Authorization")— read headers- รองรับ multipart form data อัตโนมัติ
Validation
ใช้ go-playground/validator ร่วมกับ c.BodyParser — struct tags validate:"required,min=1" ตรวจ input, return structured error response เมื่อ validation fail
Middleware Basics
app.Use(middleware) — apply globally, group.Use() — apply per group, middleware เป็น function func(c *fiber.Ctx) error เรียก c.Next() ไป handler ถัดไป return error เพื่อ abort
Built-in Middleware
Fiber มี middleware พร้อมใช้:
logger.New()— request loggingrecover.New()— recover จาก paniccors.New()— CORS headerslimiter.New()— rate limitingcompress.New()— gzip/brotli compressioncache.New()— response caching
Custom Middleware
เขียน middleware pattern: func MyAuth() fiber.Handler { return func(c *fiber.Ctx) error { ... return c.Next() } } ใช้สำหรับ authentication, request ID injection, timing, custom logging
Error Handling
- Return
errorจาก handler → Fiber จัดการด้วย default error handler fiber.NewError(404, "not found")— สร้าง error พร้อม status code- Custom error handler:
app := fiber.New(fiber.Config{ErrorHandler: customHandler})centralized error handling
Static Files & Templates
app.Static("/", "./public")— serve static files- Template engines:
html,pug,handlebarsผ่านfiber.Config{Views: engine} c.Render("index", data)— render template with data
Fasthttp Advantage
Fiber สร้างบน fasthttp แทน net/http — zero memory allocation สำหรับ hot paths, reuse buffers, เร็วกว่า net/http 5-10x ในบาง benchmarks ข้อเสีย: ไม่ compatible กับ net/http middleware ecosystem โดยตรง
Fiber vs Gin
- Fiber — Express-like API, fasthttp-based, เร็วกว่าใน benchmarks, middleware ecosystem ของตัวเอง
- Gin — net/http-based, ใหญ่กว่า community, compatible กับ standard library middleware
- เลือก Fiber เมื่อ throughput สำคัญ, เลือก Gin เมื่อต้องการ ecosystem ที่ใหญ่กว่า
WebSocket
github.com/gofiber/websocket/v2 — upgrade HTTP connection เป็น WebSocket, app.Get("/ws", websocket.New(handler)) ใช้สำหรับ real-time features เช่น chat, live updates, trading data streams
Server-Sent Events (SSE)
ใช้ c.Context().SetBodyStreamWriter() สำหรับ streaming response — ส่ง events จาก server ไป client แบบ one-way, ง่ายกว่า WebSocket สำหรับ use cases เช่น notifications, live logs
JWT Authentication
ใช้ github.com/gofiber/jwt/v3 middleware — validate JWT token จาก Authorization header, extract claims, set ใน c.Locals("user") สำหรับ downstream handlers
Rate Limiting
limiter.New(limiter.Config{Max: 100, Expiration: 1 * time.Minute}) — จำกัด requests per window, configurable per IP/key, ใช้ memory store (default) หรือ Redis store สำหรับ distributed
Prefork Mode
app.Listen(":3000", fiber.Config{Prefork: true}) — spawn multiple processes (เหมือน Node.js cluster) ใช้ SO_REUSEPORT ให้แต่ละ process accept connections, เพิ่ม throughput บน multi-core machines
Hooks & Lifecycle
app.Hooks().OnListen(func() error { ... })— run เมื่อ server start listeningapp.Hooks().OnShutdown(func() error { ... })— cleanup เมื่อ shutdown- ใช้จัดการ graceful shutdown, connection draining, resource cleanup
Testing
ใช้ app.Test(req) — ส่ง *http.Request เข้า app โดยไม่ต้อง start server, return *http.Response สำหรับ assert, เร็วมากเพราะ in-memory ไม่ผ่าน network
Performance Optimization
fiber.Config{Prefork: true}— multi-process modecompress.New()— response compressioncache.New()— response caching per route- Tune
ReadBufferSize,WriteBufferSize,Concurrencyใน config - ใช้
sync.Poolสำหรับ frequently allocated objects
ปัญหาที่เจอบ่อย & วิธีแก้
Ctx values หายหลัง handler จบ — fasthttp pool reuse
เก็บ c.Body() หรือ c.Params() ไว้ใช้ทีหลังแล้วได้ค่าผิดหรือ empty
สาเหตุ: Fiber สร้างบน fasthttp ที่ reuse request/response objects ผ่าน sync.Pool — ค่าที่ได้จาก c.Body(), c.Params(), c.Query() เป็น []byte ที่ชี้ไป buffer เดิม พอ request จบ buffer ถูก reuse ค่าจะเปลี่ยน
วิธีแก้:
- copy ค่าทันทีที่ได้:
body := make([]byte, len(c.Body())); copy(body, c.Body())หรือใช้string(c.Body()) - ใช้
c.BodyParser(&struct)แทนc.Body()— parse เข้า struct จะ copy ให้อัตโนมัติ - ใช้
utils.CopyString(c.Params("id"))สำหรับ string values ที่ต้องเก็บไว้ใช้นอก handler
net/http middleware ใช้กับ Fiber ไม่ได้
Middleware จาก ecosystem ของ net/http/Gin เอามาใช้กับ Fiber ตรงๆ ไม่ได้
สาเหตุ: Fiber ใช้ fasthttp ไม่ใช่ net/http — interface ต่างกัน http.Handler vs fiber.Handler, request/response objects คนละ type
วิธีแก้:
- ใช้
adaptorpackage:github.com/gofiber/adaptor/v2แปลงhttp.Handler→fiber.Handlerด้วยadaptor.HTTPHandler(netHTTPHandler) - ตรวจว่า Fiber มี built-in middleware ทดแทนหรือไม่ (cors, limiter, compress มีครบ)
- สำหรับ middleware ที่ซับซ้อน: เขียน Fiber version ใหม่อาจเร็วกว่า adapt เพราะใช้ fasthttp features ได้เต็มที่
BodyParser ไม่ parse — struct fields เป็น zero value
c.BodyParser(&req) ไม่ error แต่ struct fields ยังเป็น zero value ทั้งหมด
สาเหตุ: ลืมใส่ json:"field_name" struct tags, Content-Type header ไม่ตรง (ส่ง JSON แต่ไม่มี Content-Type: application/json), หรือ struct fields เป็น unexported (ตัวเล็กนำหน้า)
วิธีแก้:
- ตรวจ struct tags:
type Req struct { Name string \json:"name"` }` — ต้องมี json tag - ตรวจ Content-Type header ของ request — Fiber ใช้ Content-Type เลือก parser (JSON, XML, form)
- ตรวจว่า struct fields เป็น exported (ตัวใหญ่นำหน้า)
- ใช้
c.Body()debug ดู raw body ที่ได้รับจริง
Prefork mode กับ in-memory state ไม่ sync
ใช้ Prefork แล้ว state เช่น rate limit counter หรือ cache ไม่ตรงกันระหว่าง processes
สาเหตุ: Prefork spawn หลาย OS processes — แต่ละ process มี memory space แยกกัน in-memory store (map, sync.Map) ไม่ share ข้าม processes
วิธีแก้:
- ใช้ external store แทน in-memory: Redis สำหรับ rate limiting, caching, session
limiter.New(limiter.Config{Storage: redisStore})— ใช้ Redis store แทน memory store- ถ้าไม่ต้องการ shared state: ปิด Prefork แล้วใช้ goroutines (default) ซึ่ง share memory ได้
Error handler ไม่จับ panic
Panic ใน handler ทำ process crash แทนที่จะ return 500
สาเหตุ: ลืมใส่ recover middleware, หรือใส่ผิดลำดับ (ต้องอยู่ก่อน routes) — Fiber ไม่ recover panic by default ต่างจาก gin.Default() ที่มี recovery built-in
วิธีแก้:
- ใส่
app.Use(recover.New())ก่อน route definitions ทุกตัว - สำหรับ custom recovery:
recover.New(recover.Config{EnableStackTrace: true})เพื่อ log stack trace - เขียน custom error handler ใน
fiber.Config{ErrorHandler: func(c *fiber.Ctx, err error) error { ... }}จัดการ error format รวมศูนย์
WebSocket connection ค้าง / memory leak
WebSocket connections สะสมจน server กิน memory เพิ่มขึ้นเรื่อยๆ
สาเหตุ: ไม่ handle connection close properly — client disconnect แต่ server ไม่ detect, ไม่มี ping/pong heartbeat, goroutine ที่อ่าน/เขียน WebSocket ไม่ถูก cleanup
วิธีแก้:
- ตั้ง read/write deadline: ถ้าไม่มี activity ภายใน timeout ให้ close connection
- ใช้ ping/pong:
conn.SetPongHandler()+ periodic ping เพื่อ detect dead connections - Track active connections ใน map + cleanup เมื่อ connection close
- ใช้
defer conn.Close()ทุกครั้งที่ accept connection ใหม่