Back to Fiber

📎 Bind

docs/api/bind.md

3.2.026.2 KB
Original Source

Bindings parse request and response bodies, query parameters, cookies, and more into structs.

:::info 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:

  1. URL Parameters (URI)
  2. Request Body (e.g., JSON or form data)
  3. Query Parameters
  4. Request Headers
  5. Cookies

:::info 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. :::

go
func (b *Bind) All(out any) error
go
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-TypeStruct Tag
application/x-www-form-urlencodedform
multipart/form-dataform
application/jsonjson
application/xmlxml
text/xmlxml
application/vnd.msgpackmsgpack
go
func (b *Bind) Body(out any) error
go
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:

bash
# 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".

go
func (b *Bind) CBOR(out any) error
go
// 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:

bash
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".

go
func (b *Bind) Form(out any) error
go
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:

bash
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
bash
curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000

:::info If you need to bind multipart file, you can use *multipart.FileHeader, *[]*multipart.FileHeader or []*multipart.FileHeader as a field type. :::

go
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:

bash
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".

go
func (b *Bind) JSON(out any) error
go
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:

bash
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 msgpack struct tags by default. If you want to use other libraries, you may need to update the struct tags accordingly.

go
func (b *Bind) MsgPack(out any) error
go
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:

bash
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".

go
func (b *Bind) XML(out any) error
go
// 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:

bash
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000

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".

go
func (b *Bind) Cookie(out any) error
go
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:

bash
curl --cookie "name=Joseph; age=23; job=true" http://localhost:8000/

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".

go
func (b *Bind) Header(out any) error
go
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:

bash
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".

go
func (b *Bind) Query(out any) error
go
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:

bash
curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat"

:::info 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:

FormatExampleRequires EnableSplittingOnParsers
Repeated key?colors=red&colors=blueNo
Bracket notation?colors[]=red&colors[]=blueNo
Comma-separated?colors=red,blueYes
Indexed bracket notation?posts[0][title]=Hello&posts[1][title]=WorldNo
Nested bracket notation?preferences[tags]=golang,apiNo (comma splitting: Yes)
Repeated Key

The most common approach. Repeat the same query key for each value:

text
GET /api?colors=red&colors=blue&colors=green
go
type Filter struct {
    Colors []string `query:"colors"`
}
// Result: Colors = ["red", "blue", "green"]
bash
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:

text
GET /api?colors[]=red&colors[]=blue&colors[]=green
go
type Filter struct {
    Colors []string `query:"colors"`
}
// Result: Colors = ["red", "blue", "green"]
bash
curl "http://localhost:3000/api?colors[]=red&colors[]=blue&colors[]=green"

:::note 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.

text
GET /api?colors=red,blue,green
go
type Filter struct {
    Colors []string `query:"colors"`
}
go
// 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.

go
// GET /api?colors=red,blue,green
// Result: Colors = ["red,blue,green"]  ← single element
bash
curl "http://localhost:3000/api?colors=red,blue,green"

You can also mix comma-separated values with repeated keys when splitting is enabled:

text
GET /api?hobby=soccer&hobby=basketball,football
go
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:

text
GET /api?posts[0][title]=Hello&posts[0][author]=Alice&posts[1][title]=World&posts[1][author]=Bob
go
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"}]
bash
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:

text
GET /api?preferences[tags]=golang,api
go
type Preferences struct {
    Tags *[]string `query:"tags"`
}

type Profile struct {
    Prefs *Preferences `query:"preferences"`
}
// With EnableSplittingOnParsers: true
// Result: *Prefs.Tags = ["golang", "api"]
bash
curl "http://localhost:3000/api?preferences[tags]=golang,api"

:::note 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".

go
func (b *Bind) RespHeader(out any) error
go
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:

bash
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".

go
func (b *Bind) URI(out any) error
go
// 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(&param); 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.

go
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):

go
// 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.

go
func (b *Bind) Custom(name string, dest any) error
go
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

go
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.

go
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).

go
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.

go
func SetParserDecoder(parserConfig binder.ParserConfig)

binder.ParserConfig has the following fields:

go
type ParserConfig struct {
    IgnoreUnknownKeys bool
    ParserType        []ParserType
    ZeroEmpty         bool
    SetAliasTag       string
}

type ParserType struct {
    CustomType any
    Converter  func(string) reflect.Value
}
go

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.

go
type StructValidator interface {
    Validate(out any) error
}

Setup Your Validator in the Config

go
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

go
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).
go
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:

bash
curl "http://localhost:3000/?pass=doe"