Episode ini membahas pengolahan input di Fiber v3: binding body, query, URI, dan header ke struct dengan tag json, form, dan query; paket extractor untuk mengambil nilai secara deklaratif dengan fallback; serta validasi input memakai go-playground/validator.

Di episode 4 kalian belajar membaca input secara manual lewat c.Params dan c.Query. Episode 5 naik satu level: binding, extractors, dan validation — memetakan input ke struct, mengekstrak nilai deklaratif, dan memastikan data valid sebelum diproses.
Cara paling umum: bind body JSON ke struct dengan tag json. Fiber juga mengenali form, xml, dan msgpack sesuai Content-Type:
type User struct {
Name string `json:"name" form:"name"`
Age int `json:"age" form:"age"`
}
app.Post("/users", func(c fiber.Ctx) error {
var user User
if err := c.Bind().Body(&user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
return c.JSON(user)
})c.Bind().Body(&user) membaca body, memilih parser berdasarkan Content-Type, dan mengisi struct. Jika payload JSON, tag json yang dipakai; jika form, tag form. Keduanya bisa ditulis bersamaan seperti contoh di atas.
Binding tidak terbatas pada body. Sumber lain juga bisa di-bind ke struct yang sama:
type Filter struct {
Search string `query:"search"`
Tags []string `query:"tags"`
}
type ProductParam struct {
ID uint `uri:"id"`
}
app.Get("/products", func(c fiber.Ctx) error {
var f Filter
if err := c.Bind().Query(&f); err != nil {
return err
}
return c.JSON(f)
})
app.Get("/products/:id", func(c fiber.Ctx) error {
var p ProductParam
if err := c.Bind().URI(&p); err != nil {
return err
}
return c.JSON(p)
})c.Bind().Query(&f) memetakan query parameter ke tag query, dan c.Bind().URI(&p) memetakan path parameter ke tag uri. Untuk header, tersedia c.Bind().Header(...) dengan tag header, dan c.Bind().Cookie(...) untuk cookie. Jika ingin semua sumber sekaligus, c.Bind().All(&struct) mengikuti urutan URI, body, query, header, lalu cookie.
Paket github.com/gofiber/fiber/v3/extractors menyediakan fungsi ekstraksi yang bisa dirangkai. Ini dipakai middleware seperti session dan JWT, tapi bisa juga untuk keperluan kalian sendiri:
import "github.com/gofiber/fiber/v3/extractors"
apiKey := extractors.Chain(
extractors.FromHeader("X-API-Key"),
extractors.FromQuery("api_key"),
extractors.FromCookie("api_key"),
)
app.Use(func(c fiber.Ctx) error {
key, err := apiKey.Extract(c)
if err != nil {
return c.Status(fiber.StatusUnauthorized).SendString("API key dibutuhkan")
}
c.Locals("api_key", key)
return c.Next()
})extractors.Chain(...) mencoba header X-API-Key dulu, lalu query api_key, lalu cookie api_key. Jika semuanya tidak ada, .Extract(c) mengembalikan error. Pola ini membuat logika ekstraksi reusable dan bisa dipakai di banyak middleware.
Fiber tidak meng-invent ulang validator. Ia memakai interface StructValidator dan mengintegrasikannya dengan go-playground/validator:
import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v3"
)
type structValidator struct {
validate *validator.Validate
}
func (v *structValidator) Validate(out any) error {
return v.validate.Struct(out)
}
func main() {
app := fiber.New(fiber.Config{
StructValidator: &structValidator{validate: validator.New()},
})
}Dengan fiber.Config{StructValidator: ...}, setiap binding akan otomatis menjalankan validasi jika struct memakai tag validate.
type RegisterRequest struct {
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8"`
Age int `json:"age" validate:"gte=18,lte=99"`
}
app.Post("/register", func(c fiber.Ctx) error {
var req RegisterRequest
if err := c.Bind().JSON(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
return c.Status(fiber.StatusCreated).JSON(req)
})validate:"required,email" memastikan field wajib dan berbentuk email; min=8 menuntut password minimal 8 karakter; gte=18,lte=99 membatasi rentang umur. c.Bind().JSON(&req) menjalankan validasi otomatis dan mengembalikan error jika ada field yang melanggar.
Secara default, error binding bisa diperiksa dengan errors.As untuk mendapatkan detail sumber kegagalan:
var req struct {
ID int `uri:"id"`
Name string `json:"name"`
}
if err := c.Bind().All(&req); err != nil {
var be *fiber.BindError
if errors.As(err, &be) && be.Source == fiber.BindSourceURI {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "resource tidak ditemukan",
})
}
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "request tidak valid",
})
}*fiber.BindError berisi Source dan Field yang menjelaskan dari mana kegagalan berasal. Ada pula WithAutoHandling() yang otomatis mengembalikan status 400 — cocok jika kalian tidak butuh kontrol detail. Perlu dicatat: error validasi tidak dibungkus BindError, jadi gunakan errors.As untuk membedakannya.
curl -X POST -H "Content-Type: application/json" --data '{"name":"Budi","age":30}' http://localhost:3000/users
curl -X POST -H "Content-Type: application/json" \
--data '{"email":"budi@example.com","password":"rahasia123","age":30}' http://localhost:3000/registerInti yang harus dibawa pulang:
c.Bind().Body(), .Query(), .URI(), .Header(), dan .Cookie() memetakan input ke struct.json, form, query, uri, header, cookie.c.Bind().All() menggabungkan semua sumber dengan urutan URI, body, query, header, cookie.extractors menyediakan ekstraksi deklaratif dengan Chain dan fallback.fiber.Config{StructValidator} dengan tag validate.*fiber.BindError membantu membedakan sumber error binding.Di episode 6 selanjutnya kita membahas middleware dan hooks — menulis middleware sendiri dengan app.Use, memahami eksekusi berurutan dan c.Next(), serta memakai lifecycle hooks v3 seperti OnListen, OnPreShutdown, dan OnPostShutdown.