📎 Bind
Bindings parse request and response bodies, query parameters, cookies, and more into structs.
Binder-returned values are valid only within the handler. To keep them, copy the data
or enable the Immutable setting. Read more...
Binders
All
The All function binds data from URL parameters, the request body, query parameters, headers, and cookies into out. Sources are applied in the following order using struct field tags.
Precedence Order
The binding sources have the following precedence:
- URL Parameters (URI)
- Request Body (e.g., JSON or form data)
- Query Parameters
- Request Headers
- Cookies
The request body is only included as a binding source when the request has both a non-empty body and a non-empty Content-Type header.
func (b *Bind) All(out any) error
type User struct {
Name string `query:"name" json:"name" form:"name"`
Email string `json:"email" form:"email"`
Role string `header:"X-User-Role"`
SessionID string `json:"session_id" cookie:"session_id"`
ID int `uri:"id" query:"id" json:"id" form:"id"`
}
app.Post("/users", func(c fiber.Ctx) error {
user := new(User)
if err := c.Bind().All(user); err != nil {
return err
}
// All available data is now bound to the user struct
return c.JSON(user)
})
Body
Binds the request body to a struct.
Use tags that match the content type. For example, to parse a JSON body with a Pass field, declare json:"pass".
| Content-Type | Struct Tag |
|---|---|
application/x-www-form-urlencoded | form |
multipart/form-data | form |
application/json | json |
application/xml | xml |
text/xml | xml |
application/vnd.msgpack | msgpack |
func (b *Bind) Body(out any) error
type Person struct {
Name string `json:"name" xml:"name" form:"name" msgpack:"name"`
Pass string `json:"pass" xml:"pass" form:"pass" msgpack:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Body(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Test the handler with these curl commands:
# JSON
curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000
# MsgPack
curl -X POST -H "Content-Type: application/vnd.msgpack" --data-binary $'\x82\xa4name\xa4john\xa4pass\xa3doe' localhost:3000
# XML
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000
# Form URL-Encoded
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
# Multipart Form
curl -X POST -F name=john -F pass=doe http://localhost:3000
CBOR
Note: Before using any CBOR-related features, make sure to follow the CBOR setup instructions.
Binds the request CBOR body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a CBOR body with a field called Pass, you would use a struct field with cbor:"pass".
func (b *Bind) CBOR(out any) error
// Field names should start with an uppercase letter
type Person struct {
Name string `cbor:"name"`
Pass string `cbor:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().CBOR(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Test the defaults with this curl command:
curl -X POST -H "Content-Type: application/cbor" --data "\xa2dnamedjohndpasscdoe" localhost:3000
Form
Binds the request or multipart form body data to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a form body with a field called Pass, you would use a struct field with form:"pass".
func (b *Bind) Form(out any) error
type Person struct {
Name string `form:"name"`
Pass string `form:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Form(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Run tests with the following curl commands for both application/x-www-form-urlencoded and multipart/form-data:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000
If you need to bind multipart file, you can use *multipart.FileHeader, *[]*multipart.FileHeader or []*multipart.FileHeader as a field type.
type Person struct {
Name string `form:"name"`
Pass string `form:"pass"`
Avatar *multipart.FileHeader `form:"avatar"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Form(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
log.Println(p.Avatar.Filename) // file.txt
// ...
})
Run tests with the following curl command:
curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" -F 'avatar=@filename' localhost:3000
JSON
Binds the request JSON body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field with json:"pass".
func (b *Bind) JSON(out any) error
type Person struct {
Name string `json:"name"`
Pass string `json:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().JSON(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Run tests with the following curl command:
curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000
MsgPack
Note: Before using any MsgPack-related features, make sure to follow the MsgPack setup instructions.
Binds the request MsgPack body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a Msgpack body with a field called Pass, you would use a struct field with msgpack:"pass".
Our library uses shamaton-msgpack which uses
msgpackstruct tags by default. If you want to use other libraries, you may need to update the struct tags accordingly.
func (b *Bind) MsgPack(out any) error
type Person struct {
Name string `msgpack:"name"`
Pass string `msgpack:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().MsgPack(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Run tests with the following curl command:
curl -X POST -H "Content-Type: application/vnd.msgpack" --data-binary $'\x82\xa4name\xa4john\xa4pass\xa3doe' localhost:3000
XML
Binds the request XML body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse an XML body with a field called Pass, you would use a struct field with xml:"pass".
func (b *Bind) XML(out any) error
// Field names should start with an uppercase letter
type Person struct {
Name string `xml:"name"`
Pass string `xml:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().XML(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
Run tests with the following curl command:
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000
Cookie
This method is similar to Body Binding, but for cookie parameters.
It is important to use the struct tag cookie. For example, if you want to parse a cookie with a field called Age, you would use a struct field with cookie:"age".
func (b *Bind) Cookie(out any) error
type Person struct {
Name string `cookie:"name"`
Age int `cookie:"age"`
Job bool `cookie:"job"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Cookie(p); err != nil {
return err
}
log.Println(p.Name) // Joseph
log.Println(p.Age) // 23
log.Println(p.Job) // true
})
Run tests with the following curl command:
curl --cookie "name=Joseph; age=23; job=true" http://localhost:8000/
Header
This method is similar to Body Binding, but for request headers.
It is important to use the struct tag header. For example, if you want to parse a request header with a field called Pass, you would use a struct field with header:"pass".
func (b *Bind) Header(out any) error
type Person struct {
Name string `header:"name"`
Pass string `header:"pass"`
Products []string `header:"products"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Header(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
log.Println(p.Products) // [shoe hat]
// ...
})
Run tests with the following curl command:
curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat"
Query
This method is similar to Body Binding, but for query parameters.
It is important to use the struct tag query. For example, if you want to parse a query parameter with a field called Pass, you would use a struct field with query:"pass".
func (b *Bind) Query(out any) error
type Person struct {
Name string `query:"name"`
Pass string `query:"pass"`
Products []string `query:"products"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Query(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// Depending on fiber.Config{EnableSplittingOnParsers: false} - default
log.Println(p.Products) // ["shoe,hat"]
// With fiber.Config{EnableSplittingOnParsers: true}
// log.Println(p.Products) // ["shoe", "hat"]
// ...
})
Run tests with the following curl command:
curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat"
For more parser settings, please refer to Config
Array Query Parameters
Fiber supports several formats for passing array values via query parameters. The following table gives an overview:
| Format | Example | Requires EnableSplittingOnParsers |
|---|---|---|
| Repeated key | ?colors=red&colors=blue | No |
| Bracket notation | ?colors[]=red&colors[]=blue | No |
| Comma-separated | ?colors=red,blue | Yes |
| Indexed bracket notation | ?posts[0][title]=Hello&posts[1][title]=World | No |
| Nested bracket notation | ?preferences[tags]=golang,api | No (comma splitting: Yes) |
Repeated Key
The most common approach. Repeat the same query key for each value:
GET /api?colors=red&colors=blue&colors=green
type Filter struct {
Colors []string `query:"colors"`
}
// Result: Colors = ["red", "blue", "green"]
curl "http://localhost:3000/api?colors=red&colors=blue&colors=green"
Bracket Notation
Append [] to the key name. This is common in PHP-style and JavaScript frameworks:
GET /api?colors[]=red&colors[]=blue&colors[]=green
type Filter struct {
Colors []string `query:"colors"`
}
// Result: Colors = ["red", "blue", "green"]
curl "http://localhost:3000/api?colors[]=red&colors[]=blue&colors[]=green"
The struct field tag stays query:"colors" (without brackets). Fiber strips the [] automatically.
Comma-Separated Values
Pass multiple values in a single parameter, separated by commas. This format requires EnableSplittingOnParsers to be set to true.
GET /api?colors=red,blue,green
type Filter struct {
Colors []string `query:"colors"`
}
// EnableSplittingOnParsers is required for comma splitting
app := fiber.New(fiber.Config{
EnableSplittingOnParsers: true,
})
// Result: Colors = ["red", "blue", "green"]
Without EnableSplittingOnParsers, the entire string "red,blue,green" is treated as a single element.
// GET /api?colors=red,blue,green
// Result: Colors = ["red,blue,green"] ← single element
curl "http://localhost:3000/api?colors=red,blue,green"
You can also mix comma-separated values with repeated keys when splitting is enabled:
GET /api?hobby=soccer&hobby=basketball,football
type Query struct {
Hobby []string `query:"hobby"`
}
// With EnableSplittingOnParsers: true
// Result: Hobby = ["soccer", "basketball", "football"] ← 3 elements
Indexed Bracket Notation (Nested Structs)
Use indexed brackets to bind arrays of nested structs:
GET /api?posts[0][title]=Hello&posts[0][author]=Alice&posts[1][title]=World&posts[1][author]=Bob
type Post struct {
Title string `query:"title"`
Author string `query:"author"`
}
type Request struct {
Posts []Post `query:"posts"`
}
// Result: Posts = [{Title: "Hello", Author: "Alice"}, {Title: "World", Author: "Bob"}]
curl "http://localhost:3000/api?posts[0][title]=Hello&posts[0][author]=Alice&posts[1][title]=World&posts[1][author]=Bob"
Nested Bracket Notation (Without Index)
Use bracket notation to access fields of a nested struct:
GET /api?preferences[tags]=golang,api
type Preferences struct {
Tags *[]string `query:"tags"`
}
type Profile struct {
Prefs *Preferences `query:"preferences"`
}
// With EnableSplittingOnParsers: true
// Result: *Prefs.Tags = ["golang", "api"]
curl "http://localhost:3000/api?preferences[tags]=golang,api"
Pointer fields (*[]string, *Preferences) let you distinguish between a missing parameter (nil) and an empty one. When the parameter is present, Fiber allocates the pointer automatically.
RespHeader
This method is similar to Body Binding, but for response headers.
It is important to use the struct tag respHeader. For example, if you want to parse a response header with a field called Pass, you would use a struct field with respHeader:"pass".
func (b *Bind) RespHeader(out any) error
type Person struct {
Name string `respHeader:"name"`
Pass string `respHeader:"pass"`
Products []string `respHeader:"products"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().RespHeader(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
log.Println(p.Products) // [shoe hat]
// ...
})
Run tests with the following curl command:
curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat"
URI
This method is similar to Body Binding, but for path parameters.
It is important to use the struct tag uri. For example, if you want to parse a path parameter with a field called Pass, you would use a struct field with uri:"pass".
func (b *Bind) URI(out any) error
// GET http://example.com/user/111
app.Get("/user/:id", func(c fiber.Ctx) error {
param := struct {
ID uint `uri:"id"`
}{}
if err := c.Bind().URI(¶m); err != nil {
return err
}
// ...
return c.SendString(fmt.Sprintf("User ID: %d", param.ID))
})
BindError
When a bind method fails to parse (e.g. invalid JSON, bad type conversion), the behavior depends on the error-handling mode. In manual handling (the default), the binder returns a *BindError wrapping the underlying error — use errors.As to extract it and branch on the binding source or field. In automatic handling (enabled via WithAutoHandling), parse failures are instead converted to a *fiber.Error with HTTP status 400; *BindError is never surfaced to the caller in that mode. If you are using WithAutoHandling, check for *fiber.Error or an HTTP 400 response rather than using errors.As for *BindError.
type BindError struct {
Source string // "uri", "query", "body", "header", "cookie", or "respHeader"
Field string // struct field or tag key that failed (best-effort, may be empty)
Err error // underlying error; use errors.As to inspect
}
Source constants: BindSourceURI, BindSourceQuery, BindSourceHeader, BindSourceCookie, BindSourceBody, BindSourceRespHeader.
Branching on source
Use errors.As to extract *BindError and branch on Source for RFC-correct status codes (e.g. 404 for URI failures vs 400 for body/query):
// With manual handling mode (default behavior)
// Will not work with WithAutoHandling()
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": "not found"})
}
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request"})
}
Validation vs binding errors
Validation errors (from StructValidator) are not wrapped in BindError. Use errors.As(err, &be) to distinguish: it succeeds only for parsing/binding failures, not for validation failures.
Custom
To use custom binders, you have to use this method.
You can register them using the RegisterCustomBinder method of the Fiber instance.
func (b *Bind) Custom(name string, dest any) error
app := fiber.New()
// My custom binder
type customBinder struct{}
func (cb *customBinder) Name() string {
return "custom"
}
func (cb *customBinder) MIMETypes() []string {
return []string{"application/yaml"}
}
func (cb *customBinder) Parse(c fiber.Ctx, out any) error {
// parse YAML body
return yaml.Unmarshal(c.Body(), out)
}
// Register custom binder
app.RegisterCustomBinder(&customBinder{})
type User struct {
Name string `yaml:"name"`
}
// curl -X POST http://localhost:3000/custom -H "Content-Type: application/yaml" -d "name: John"
app.Post("/custom", func(c fiber.Ctx) error {
var user User
// Use Custom binder by name
if err := c.Bind().Custom("custom", &user); err != nil {
return err
}
return c.JSON(user)
})
Internally, custom binders are also used in the Body method.
The MIMETypes method is used to check if the custom binder should be used for the given content type.
Options
For more control over error handling, you can use the following methods.
WithAutoHandling
If you want to handle binder errors automatically, you can use WithAutoHandling.
If there's an error, it will return the error and set HTTP status to 400 Bad Request.
This function does NOT panic therefore you must still return on error explicitly
func (b *Bind) WithAutoHandling() *Bind
WithoutAutoHandling
To handle binder errors manually, you can use the WithoutAutoHandling method.
It's the default behavior of the binder.
func (b *Bind) WithoutAutoHandling() *Bind
SkipValidation
To enable or disable validation for the current bind chain, use SkipValidation.
By default, validation is enabled (skip = false).
func (b *Bind) SkipValidation(skip bool) *Bind
SetParserDecoder
Allows you to configure the BodyParser/QueryParser decoder based on schema options, providing the possibility to add custom types for parsing.
func SetParserDecoder(parserConfig binder.ParserConfig)
binder.ParserConfig has the following fields:
type ParserConfig struct {
IgnoreUnknownKeys bool
ParserType []ParserType
ZeroEmpty bool
SetAliasTag string
}
type ParserType struct {
CustomType any
Converter func(string) reflect.Value
}
type CustomTime time.Time
// String returns the time in string format
func (ct *CustomTime) String() string {
t := time.Time(*ct).String()
return t
}
// Converter for CustomTime type with format "2006-01-02"
var timeConverter = func(value string) reflect.Value {
fmt.Println("timeConverter:", value)
if v, err := time.Parse("2006-01-02", value); err == nil {
return reflect.ValueOf(CustomTime(v))
}
return reflect.Value{}
}
customTime := binder.ParserType{
CustomType: CustomTime{},
Converter: timeConverter,
}
// Add custom type to the Decoder settings
binder.SetParserDecoder(binder.ParserConfig{
IgnoreUnknownKeys: true,
ParserType: []binder.ParserType{customTime},
ZeroEmpty: true,
})
// Example using CustomTime with non-RFC3339 format
type Demo struct {
Date CustomTime `form:"date" query:"date"`
Title string `form:"title" query:"title"`
Body string `form:"body" query:"body"`
}
app.Post("/body", func(c fiber.Ctx) error {
var d Demo
if err := c.Bind().Body(&d); err != nil {
return err
}
fmt.Println("d.Date:", d.Date.String())
return c.JSON(d)
})
app.Get("/query", func(c fiber.Ctx) error {
var d Demo
if err := c.Bind().Query(&d); err != nil {
return err
}
fmt.Println("d.Date:", d.Date.String())
return c.JSON(d)
})
// Run tests with the following curl commands:
# Body Binding
curl -X POST -F title=title -F body=body -F date=2021-10-20 http://localhost:3000/body
# Query Binding
curl -X GET "http://localhost:3000/query?title=title&body=body&date=2021-10-20"
Validation
Validation is also possible with the binding methods. You can specify your validation rules using the validate struct tag.
Specify your struct validator in the config.
The validator must implement the StructValidator interface, which requires a Validate method that takes an any type and returns an error.
type StructValidator interface {
Validate(out any) error
}
Setup Your Validator in the Config
import "github.com/go-playground/validator/v10"
type structValidator struct {
validate *validator.Validate
}
// Validate method implementation
func (v *structValidator) Validate(out any) error {
return v.validate.Struct(out)
}
// Setup your validator in the Fiber config
app := fiber.New(fiber.Config{
StructValidator: &structValidator{validate: validator.New()},
})
Fiber only runs StructValidator for struct destinations (or pointers to structs).
Binding into maps and other non-struct types skips the validator step.
Usage of Validation in Binding Methods
type Person struct {
Name string `json:"name" validate:"required"`
Age int `json:"age" validate:"gte=18,lte=60"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().JSON(p); err != nil { // Receives validation errors
return err
}
})
Default Fields
You can set default values for fields in the struct by using the default struct tag. Supported types:
bool- Float variants (
float32,float64) - Int variants (
int,int8,int16,int32,int64) - Uint variants (
uint,uint8,uint16,uint32,uint64) string- A slice of the above types. Use
|to separate slice items. - A pointer to one of the above types (pointers to slices and slices of pointers are not supported).
type Person struct {
Name string `query:"name,default:john"`
Pass string `query:"pass"`
Products []string `query:"products,default:shoe|hat"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().Query(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
log.Println(p.Products) // ["shoe", "hat"]
// ...
})
Run tests with the following curl command:
curl "http://localhost:3000/?pass=doe"