Skip to main content

Overview

While JSON is the most common format for web APIs, binary serialization formats can provide significant performance and size benefits. Fiber supports multiple formats out of the box.

MessagePack (MsgPack)

MessagePack is an efficient binary serialization format that’s like JSON but faster and smaller.

Benefits

  • 30-50% smaller than JSON
  • Faster encoding/decoding
  • Type preservation
  • Wide language support

Setup

Fiber doesn’t bundle MsgPack (it’s not in Go’s standard library), so choose a library: Recommended Libraries:
go get github.com/shamaton/msgpack/v3

Configuration

import (
    "github.com/gofiber/fiber/v3"
    "github.com/shamaton/msgpack/v3"
)

app := fiber.New(fiber.Config{
    MsgPackEncoder: msgpack.Marshal,
    MsgPackDecoder: msgpack.Unmarshal,
})

Basic Usage

type User struct {
    Name  string `msgpack:"name"`
    Age   int    `msgpack:"age"`
    Email string `msgpack:"email"`
}

app.Post("/msgpack", func(c fiber.Ctx) error {
    user := new(User)
    
    // Bind MsgPack request body
    if err := c.Bind().MsgPack(user); err != nil {
        return err
    }
    
    // Respond with MsgPack (Content-Type: application/vnd.msgpack)
    return c.MsgPack(user)
})

Auto Format

Fiber can automatically choose MsgPack based on the Accept header:
app.Get("/data", func(c fiber.Ctx) error {
    data := map[string]interface{}{
        "name": "John",
        "age":  30,
    }
    
    // Returns JSON or MsgPack based on Accept header
    return c.AutoFormat(data)
})

// Client request with Accept: application/vnd.msgpack -> MsgPack response
// Client request with Accept: application/json -> JSON response

Using vmihailenco/msgpack

Alternative library with more features:
import (
    "github.com/gofiber/fiber/v3"
    "github.com/vmihailenco/msgpack/v5"
)

app := fiber.New(fiber.Config{
    MsgPackEncoder: msgpack.Marshal,
    MsgPackDecoder: msgpack.Unmarshal,
})

type Product struct {
    ID          int       `msgpack:"id"`
    Name        string    `msgpack:"name"`
    Price       float64   `msgpack:"price"`
    CreatedAt   time.Time `msgpack:"created_at"`
}

app.Get("/products/:id", func(c fiber.Ctx) error {
    product := Product{
        ID:        1,
        Name:      "Widget",
        Price:     19.99,
        CreatedAt: time.Now(),
    }
    
    return c.MsgPack(product)
})

CBOR (Concise Binary Object Representation)

CBOR is a binary format similar to MessagePack, defined in RFC 8949.

Benefits

  • Standardized (IETF RFC)
  • Compact binary encoding
  • Supports more data types than JSON
  • Good for IoT and constrained environments

Setup

go get github.com/fxamacker/cbor/v2

Configuration

import (
    "github.com/gofiber/fiber/v3"
    "github.com/fxamacker/cbor/v2"
)

app := fiber.New(fiber.Config{
    CBOREncoder: cbor.Marshal,
    CBORDecoder: cbor.Unmarshal,
})

Basic Usage

type Sensor struct {
    DeviceID    string    `cbor:"device_id"`
    Temperature float64   `cbor:"temp"`
    Humidity    float64   `cbor:"humidity"`
    Timestamp   time.Time `cbor:"ts"`
}

app.Post("/sensor/data", func(c fiber.Ctx) error {
    sensor := new(Sensor)
    
    // Bind CBOR request body
    if err := c.Bind().CBOR(sensor); err != nil {
        return err
    }
    
    // Process sensor data
    log.Printf("Received data from %s: temp=%.2f, humidity=%.2f",
        sensor.DeviceID, sensor.Temperature, sensor.Humidity)
    
    // Respond with CBOR (Content-Type: application/cbor)
    return c.CBOR(fiber.Map{
        "status": "received",
        "timestamp": time.Now(),
    })
})

Auto Format with CBOR

app.Get("/api/data", func(c fiber.Ctx) error {
    data := map[string]interface{}{
        "temperature": 23.5,
        "humidity":    65.2,
        "timestamp":   time.Now(),
    }
    
    // Returns JSON, CBOR, or MsgPack based on Accept header
    return c.AutoFormat(data)
})

// Accept: application/cbor -> CBOR response
// Accept: application/vnd.msgpack -> MsgPack response  
// Accept: application/json -> JSON response

XML Support

Fiber has built-in XML support using Go’s standard library:
type Book struct {
    XMLName xml.Name `xml:"book"`
    Title   string   `xml:"title"`
    Author  string   `xml:"author"`
    Year    int      `xml:"year"`
}

app.Post("/books", func(c fiber.Ctx) error {
    book := new(Book)
    
    // Bind XML request body
    if err := c.Bind().XML(book); err != nil {
        return err
    }
    
    // Respond with XML (Content-Type: application/xml)
    return c.XML(book)
})

Custom XML Encoder

For better performance, use a custom XML library:
import "github.com/clbanning/mxj/v2"

app := fiber.New(fiber.Config{
    XMLEncoder: customXMLMarshal,
    XMLDecoder: customXMLUnmarshal,
})

Form Data Binding

Fiber supports form data with automatic binding:
type LoginForm struct {
    Username string `form:"username"`
    Password string `form:"password"`
    Remember bool   `form:"remember"`
}

app.Post("/login", func(c fiber.Ctx) error {
    form := new(LoginForm)
    
    // Bind form data (application/x-www-form-urlencoded)
    if err := c.Bind().Form(form); err != nil {
        return err
    }
    
    // Process login
    return c.JSON(fiber.Map{"username": form.Username})
})

Multipart Form with Files

import "mime/multipart"

type UploadForm struct {
    Title       string                `form:"title"`
    Description string                `form:"description"`
    File        *multipart.FileHeader `form:"file"`
}

app.Post("/upload", func(c fiber.Ctx) error {
    form := new(UploadForm)
    
    // Bind multipart form data
    if err := c.Bind().Form(form); err != nil {
        return err
    }
    
    // Save file
    if err := c.SaveFile(form.File, "./uploads/"+form.File.Filename); err != nil {
        return err
    }
    
    return c.JSON(fiber.Map{
        "title": form.Title,
        "filename": form.File.Filename,
    })
})

Content Negotiation

Fiber’s AutoFormat automatically selects the best format:
app.Get("/api/users", func(c fiber.Ctx) error {
    users := []User{
        {Name: "Alice", Age: 30},
        {Name: "Bob", Age: 25},
    }
    
    // Automatically responds in the requested format
    return c.AutoFormat(users)
})

// Client headers determine response format:
// Accept: application/json -> JSON
// Accept: application/xml -> XML
// Accept: application/vnd.msgpack -> MsgPack
// Accept: application/cbor -> CBOR

Format Comparison

Size Comparison

For a typical API response with 1000 users:
FormatSizeCompression
JSON85 KBBaseline
JSON (minified)65 KB23% smaller
MessagePack48 KB43% smaller
CBOR52 KB39% smaller
Protobuf*35 KB59% smaller
*Protobuf requires schema definition

Performance Comparison

Benchmark results for encoding/decoding 1000 objects:
FormatEncodeDecodeTotal
JSON (stdlib)2.5ms3.2ms5.7ms
JSON (goccy/go-json)0.9ms1.1ms2.0ms
MessagePack0.8ms1.0ms1.8ms
CBOR1.0ms1.2ms2.2ms

When to Use What

JSON
  • Human-readable debugging needed
  • Browser/JavaScript clients
  • Wide compatibility required
  • Simple data structures
MessagePack
  • Performance is critical
  • Network bandwidth is limited
  • Server-to-server communication
  • Mobile applications
CBOR
  • IoT/embedded systems
  • Standards compliance required
  • Complex data types needed
  • Interoperability with other CBOR systems
XML
  • Legacy system integration
  • SOAP services
  • Complex document structures
  • Schema validation required

Custom Serialization

Implement custom encoding for specialized formats:
import "google.golang.org/protobuf/proto"

app := fiber.New(fiber.Config{
    // Custom format handlers
})

app.Get("/data.proto", func(c fiber.Ctx) error {
    // Assuming you have protobuf definitions
    message := &pb.UserMessage{
        Name: "John",
        Age:  30,
    }
    
    data, err := proto.Marshal(message)
    if err != nil {
        return err
    }
    
    c.Set("Content-Type", "application/x-protobuf")
    return c.Send(data)
})

Best Practices

  1. Choose the right format - JSON for web, MsgPack for performance
  2. Document your API - Specify supported formats in docs
  3. Support multiple formats - Use AutoFormat for flexibility
  4. Version your APIs - Format changes can break clients
  5. Benchmark your use case - Test with realistic data
  6. Consider compression - Gzip can reduce JSON size significantly
  7. Validate input - Always validate regardless of format

See Also

Build docs developers (and LLMs) love