1. Project Overview & Quickstart (gofiber/fiber)
File: README.md
⚙️ Installation
Fiber requires Go version 1.26 or higher to run. If you need to install or upgrade Go, visit the official Go download page. To start setting up your project, create a new directory for your project and navigate into it. Then, initialize your project with Go modules by executing the following command in your terminal:
go mod init github.com/your/repoTo learn more about Go modules and how they work, you can check out the Using Go Modules blog post.
After setting up your project, you can install Fiber with the go get command:
go get -u github.com/gofiber/fiber/v3This command fetches the Fiber package and adds it to your project's dependencies, allowing you to start building your web applications with Fiber.
⚡️ Quickstart
Getting started with Fiber is easy. Here's a basic example to create a simple web server that responds with "Hello, World 👋!" on the root path. This example demonstrates initializing a new Fiber app, setting up a route, and starting the server.
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
// Initialize a new Fiber app
app := fiber.New()
// Define a route for the GET method on the root path '/'
app.Get("/", func(c fiber.Ctx) error {
// Send a string response to the client
return c.SendString("Hello, World 👋!")
})
// Start the server on port 3000
log.Fatal(app.Listen(":3000"))
}This simple server is easy to set up and run. It introduces the core concepts of Fiber: app initialization, route definition, and starting the server. Just run this Go program, and visit http://localhost:3000 in your browser to see the message.
Zero Allocation
Fiber is optimized for high-performance, meaning values returned from fiber.Ctx are not immutable by default and will be reused across requests. As a rule of thumb, you must only use context values within the handler and must not keep any references. Once you return from the handler, any values obtained from the context will be reused in future requests. Visit our documentation to learn more.
🤖 Benchmarks
These tests are performed by TechEmpower. If you want to see all the results, please visit our Wiki.
🎯 Features
- Robust Routing
- Serve Static Files
- Extreme Performance
- Low Memory footprint
- API Endpoints
- Middleware & Next support
- Rapid server-side programming
- Template Engines
- WebSocket Support
- Socket.io Support
- Server-Sent Events
- Rate Limiter
- And much more, explore Fiber
💡 Philosophy
New gophers that make the switch from Node.js to Go are dealing with a learning curve before they can start building their web applications or microservices. Fiber, as a web framework, was created with the idea of minimalism and follows the UNIX way, so that new gophers can quickly enter the world of Go with a warm and trusted welcome.
Fiber is inspired by Express, the most popular web framework on the Internet. We combined the ease of Express and raw performance of Go. If you have ever implemented a web application in Node.js (using Express or similar), then many methods and principles will seem very common to you.
We listen to our users in issues, Discord channel and all over the Internet to create a fast, flexible and friendly Go web framework for any task, deadline and developer skill! Just like Express does in the JavaScript world.
⚠️ Limitations
- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber v3 has been tested with Go version 1.26 or higher.
- Fiber automatically adapts common
net/httphandler shapes when you register them on the router, and you can still use the adaptor middleware when you need to bridge entire apps ornet/httpmiddleware.
net/http compatibility
Fiber can run side by side with the standard library. The router accepts existing net/http handlers directly and even works with native fasthttp.RequestHandler callbacks, so you can plug in legacy endpoints without wrapping them manually:
package main
import (
"log"
"net/http"
"github.com/gofiber/fiber/v3"
)
func main() {
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := w.Write([]byte("served by net/http")); err != nil {
panic(err)
}
})
app := fiber.New()
app.Get("/", httpHandler)
// Start the server on port 3000
log.Fatal(app.Listen(":3000"))
}When you need to convert entire applications or reuse net/http middleware chains, rely on the adaptor middleware. It converts handlers and middlewares in both directions and even lets you mount a Fiber app in a net/http server.
Express-style handlers
Fiber also adapts Express-style callbacks that operate on the lightweight fiber.Req and fiber.Res helper interfaces. This lets you port middleware and route handlers from Express-inspired codebases while keeping Fiber's router features:
// Request/response handlers (2-argument)
app.Get("/", func(req fiber.Req, res fiber.Res) error {
return res.SendString("Hello from Express-style handlers!")
})
// Middleware with an error-returning next callback (3-argument)
app.Use(func(req fiber.Req, res fiber.Res, next func() error) error {
if req.IP() == "192.168.1.254" {
return res.SendStatus(fiber.StatusForbidden)
}
return next()
})
// Middleware with a no-arg next callback (3-argument)
app.Use(func(req fiber.Req, res fiber.Res, next func()) {
if req.Get("X-Skip") == "true" {
return // stop the chain without calling next
}
next()
})Note: Adapted
net/httphandlers continue to operate with the standard-library semantics. They don't get access tofiber.Ctxfeatures and incur the overhead of the compatibility layer, so nativefiber.Handlercallbacks still provide the best performance.
👀 Examples
Listed below are some of the common examples. If you want to see more code examples, please visit our Recipes repository or visit our hosted API documentation.
📖 [**Basic Routing**](https://docs.gofiber.io/#basic-routing)
package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
// GET /api/register
app.Get("/api/*", func(c fiber.Ctx) error {
msg := fmt.Sprintf("✋ %s", c.Params("*"))
return c.SendString(msg) // => ✋ register
})
// GET /flights/LAX-SFO
app.Get("/flights/:from-:to", func(c fiber.Ctx) error {
msg := fmt.Sprintf("💸 From: %s, To: %s", c.Params("from"), c.Params("to"))
return c.SendString(msg) // => 💸 From: LAX, To: SFO
})
// GET /dictionary.txt
app.Get("/:file.:ext", func(c fiber.Ctx) error {
msg := fmt.Sprintf("📃 %s.%s", c.Params("file"), c.Params("ext"))
return c.SendString(msg) // => 📃 dictionary.txt
})
// GET /john/75
app.Get("/:name/:age/:gender?", func(c fiber.Ctx) error {
msg := fmt.Sprintf("👴 %s is %s years old", c.Params("name"), c.Params("age"))
return c.SendString(msg) // => 👴 john is 75 years old
})
// GET /john
app.Get("/:name", func(c fiber.Ctx) error {
msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name"))
return c.SendString(msg) // => Hello john 👋!
})
log.Fatal(app.Listen(":3000"))
}📖 [**Route Naming**](https://docs.gofiber.io/api/app#name)
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
app.Get("/api/*", func(c fiber.Ctx) error {
msg := fmt.Sprintf("✋ %s", c.Params("*"))
return c.SendString(msg) // => ✋ register
}).Name("api")
route := app.GetRoute("api")
data, _ := json.MarshalIndent(route, "", " ")
fmt.Println(string(data))
// Prints:
// {
// "method": "GET",
// "name": "api",
// "path": "/api/*",
// "params": [
// "*1"
// ]
// }
log.Fatal(app.Listen(":3000"))
}📖 [**Serving Static Files**](https://docs.gofiber.io/api/app#static)
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/static"
)
func main() {
app := fiber.New()
// Serve static files from the "./public" directory
app.Get("/*", static.New("./public"))
// => http://localhost:3000/js/script.js
// => http://localhost:3000/css/style.css
app.Get("/prefix*", static.New("./public"))
// => http://localhost:3000/prefix/js/script.js
// => http://localhost:3000/prefix/css/style.css
// Serve a single file for any unmatched routes
app.Get("*", static.New("./public/index.html"))
// => http://localhost:3000/any/path/shows/index.html
log.Fatal(app.Listen(":3000"))
}📖 [**Middleware & Next**](https://docs.gofiber.io/api/ctx#next)
package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
// Middleware that matches any route
app.Use(func(c fiber.Ctx) error {
fmt.Println("🥇 First handler")
return c.Next()
})
// Middleware that matches all routes starting with /api
app.Use("/api", func(c fiber.Ctx) error {
fmt.Println("🥈 Second handler")
return c.Next()
})
// GET /api/list
app.Get("/api/list", func(c fiber.Ctx) error {
fmt.Println("🥉 Last handler")
return c.SendString("Hello, World 👋!")
})
log.Fatal(app.Listen(":3000"))
}📚 Show more code examples
Views Engines
Fiber defaults to the html/template when no view engine is set.
If you want to execute partials or use a different engine like amber, handlebars, mustache, or pug, etc., check out our Template package that supports multiple view engines.
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/template/pug"
)
func main() {
// Initialize a new Fiber app with Pug template engine
app := fiber.New(fiber.Config{
Views: pug.New("./views", ".pug"),
})
// Define a route that renders the "home.pug" template
app.Get("/", func(c fiber.Ctx) error {
return c.Render("home", fiber.Map{
"title": "Homepage",
"year": 1999,
})
})
log.Fatal(app.Listen(":3000"))
}Grouping Routes into Chains
📖 Group
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func middleware(c fiber.Ctx) error {
log.Println("Middleware executed")
return c.Next()
}
func handler(c fiber.Ctx) error {
return c.SendString("Handler response")
}
func main() {
app := fiber.New()
// Root API group with middleware
api := app.Group("/api", middleware) // /api
// API v1 routes
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
// API v2 routes
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
}Middleware Logger
📖 Logger
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
)
func main() {
app := fiber.New()
// Use Logger middleware
app.Use(logger.New())
// Define routes
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, Logger!")
})
log.Fatal(app.Listen(":3000"))
}Cross-Origin Resource Sharing (CORS)
📖 CORS
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
)
func main() {
app := fiber.New()
// Use CORS middleware with default settings
app.Use(cors.New())
// Define routes
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("CORS enabled!")
})
log.Fatal(app.Listen(":3000"))
}Check CORS by passing any domain in Origin header:
curl -H "Origin: http://example.com" --verbose http://localhost:3000Custom 404 Response
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
// Define routes
app.Get("/", static.New("./public"))
app.Get("/demo", func(c fiber.Ctx) error {
return c.SendString("This is a demo page!")
})
app.Post("/register", func(c fiber.Ctx) error {
return c.SendString("Registration successful!")
})
// Middleware to handle 404 Not Found
app.Use(func(c fiber.Ctx) error {
return c.SendStatus(fiber.StatusNotFound) // => 404 "Not Found"
})
log.Fatal(app.Listen(":3000"))
}JSON Response
📖 JSON
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
app := fiber.New()
// Route that returns a JSON object
app.Get("/user", func(c fiber.Ctx) error {
return c.JSON(&User{"John", 20})
// => {"name":"John", "age":20}
})
// Route that returns a JSON map
app.Get("/json", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"success": true,
"message": "Hi John!",
})
// => {"success":true, "message":"Hi John!"}
})
log.Fatal(app.Listen(":3000"))
}WebSocket Upgrade
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/websocket"
)
func main() {
app := fiber.New()
// WebSocket route
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
defer c.Close()
for {
// Read message from client
mt, msg, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", msg)
// Write message back to client
err = c.WriteMessage(mt, msg)
if err != nil {
log.Println("write:", err)
break
}
}
}))
log.Fatal(app.Listen(":3000"))
// Connect via WebSocket at ws://localhost:3000/ws
}Server-Sent Events
package main
import (
"bufio"
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v3"
"github.com/valyala/fasthttp"
)
func main() {
app := fiber.New()
// Server-Sent Events route
app.Get("/sse", func(c fiber.Ctx) error {
c.Set("Content-Type", "text/event-stream")
c.Set("Cache-Control", "no-cache")
c.Set("Connection", "keep-alive")
c.Set("Transfer-Encoding", "chunked")
c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
var i int
for {
i++
msg := fmt.Sprintf("%d - the time is %v", i, time.Now())
fmt.Fprintf(w, "data: Message: %s\n\n", msg)
fmt.Println(msg)
w.Flush()
time.Sleep(5 * time.Second)
}
})
return nil
})
log.Fatal(app.Listen(":3000"))
}Recover Middleware
📖 Recover
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/recover"
)
func main() {
app := fiber.New()
// Use Recover middleware to handle panics gracefully
app.Use(recover.New())
// Route that intentionally panics
app.Get("/", func(c fiber.Ctx) error {
panic("normally this would crash your app")
})
log.Fatal(app.Listen(":3000"))
}Using Trusted Proxy
📖 Config
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New(fiber.Config{
// Configure trusted proxies - WARNING: Only trust proxies you control
// Using TrustProxy: true with unrestricted IPs can lead to IP spoofing
TrustProxy: true,
TrustProxyConfig: fiber.TrustProxyConfig{
Proxies: []string{"10.0.0.0/8", "172.16.0.0/12"}, // Example: Internal network ranges only
},
ProxyHeader: fiber.HeaderXForwardedFor,
})
// Define routes
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Trusted Proxy Configured!")
})
log.Fatal(app.Listen(":3000"))
}🧬 Internal Middleware
Here is a list of middleware that are included within the Fiber framework.
| Middleware | Description |
|---|---|
| adaptor | Converter for net/http handlers to/from Fiber request handlers. |
| basicauth | Provides HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials. |
| cache | Intercept and cache HTTP responses. |
| compress | Compression middleware for Fiber, with support for deflate, gzip, brotli and zstd. |
| cors | Enable cross-origin resource sharing (CORS) with various options. |
| csrf | Protect from CSRF exploits. |
| earlydata | Adds support for TLS 1.3's early data ("0-RTT") feature. |
| encryptcookie | Encrypt middleware which encrypts cookie values. |
| envvar | Expose environment variables with providing an optional config. |
| etag | Allows for caches to be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. |
| expvar | Serves via its HTTP server runtime exposed variables in the JSON format. |
| favicon | Ignore favicon from logs or serve from memory if a file path is provided. |
| healthcheck | Liveness and Readiness probes for Fiber. |
| helmet | Helps secure your apps by setting various HTTP headers. |
| hostauthorization | Validates the Host header against a configurable allowlist, protecting against DNS rebinding attacks. |
| idempotency | Allows for fault-tolerant APIs where duplicate requests do not erroneously cause the same action performed multiple times on the server-side. |
| keyauth | Adds support for key based authentication. |
| limiter | Adds Rate-limiting support to Fiber. Use to limit repeated requests to public APIs and/or endpoints such as password reset. |
| logger | HTTP request/response logger. |
| paginate | Extracts pagination parameters from query strings. Supports page-based, offset-based, and cursor-based pagination with multi-field sorting. |
| pprof | Serves runtime profiling data in pprof format. |
| proxy | Allows you to proxy requests to multiple servers. |
| recover | Recovers from panics anywhere in the stack chain and handles the control to the centralized ErrorHandler. |
| redirect | Redirect middleware. |
| requestid | Adds a request ID to every request. |
| responsetime | Measures request handling duration and writes it to a configurable response header. |
| rewrite | Rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. |
| session | Session middleware. NOTE: This middleware uses our Storage package. |
| skip | Skip middleware that skips a wrapped handler if a predicate is true. |
| static | Static middleware for Fiber that serves static files such as images, CSS, and JavaScript. |
| timeout | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. |
🧬 External Middleware
List of externally hosted middleware modules and maintained by the Fiber team.
| Middleware | Description |
|---|---|
| contrib | Third-party middlewares |
| storage | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. |
| template | This package contains 9 template engines that can be used with Fiber. |
🕶️ Awesome List
For more articles, middlewares, examples, or tools, check our awesome list.
👍 Contribute
If you want to say Thank You and/or support the active development of Fiber:
- Add a GitHub Star to the project.
- Tweet about the project on your 𝕏 (Twitter).
- Write a review or tutorial on Medium, Dev.to or your personal blog.
- Support the project by becoming a GitHub Sponsor.
💻 Development
To ensure your contributions are ready for a Pull Request, please use the following Makefile commands. These tools help maintain code quality and consistency.
- make help: Display available commands.
- make audit: Conduct quality checks.
- make benchmark: Benchmark code performance.
- make coverage: Generate test coverage report.
- make format: Automatically format code.
- make lint: Run lint checks.
- make test: Execute all tests.
- make tidy: Tidy dependencies.
Run these commands to ensure your code adheres to project standards and best practices.
☕ Supporters
Fiber is an open-source project that runs on donations to pay the bills, e.g., our domain name, GitBook, Netlify, and serverless hosting. If you want to support Fiber, please sponsor the organization via ⭐ GitHub Sponsors.
📅 Monthly Sponsors
| 🔥 Fiber Guardian | |
|---|---|
| ☕ Fiber Supporter | |
| 🪴 Fiber Friend |
🎁 One-time Sponsors
| 🚀 Fiber Hero | |
|---|---|
| 🪴 Fiber Friend |
💻 Code Contributors
⭐️ Stargazers
🧾 License
Copyright (c) 2019-present Fenny and Contributors. Fiber is free and open-source software licensed under the MIT License. Official logo was created by Vic Shóstak and distributed under Creative Commons license (CC BY-SA 4.0 International).
File: addon/retry/README.md
Retry Addon
Retry addon for Fiber designed to apply retry mechanism for unsuccessful network
operations. This addon uses an exponential backoff algorithm with jitter. It calls the function multiple times and tries
to make it successful. If all calls are failed, then, it returns an error. It adds a jitter at each retry step because adding
a jitter is a way to break synchronization across the client and avoid collision.
Table of Contents
- Retry Addon
- Table of Contents
- Signatures
- Examples
- Default Config
- Custom Config
- Config
- Default Config Example
Signatures
func NewExponentialBackoff(config ...retry.Config) *retry.ExponentialBackoffExamples
package main
import (
"fmt"
"github.com/gofiber/fiber/v3/addon/retry"
"github.com/gofiber/fiber/v3/client"
)
func main() {
expBackoff := retry.NewExponentialBackoff(retry.Config{})
// Local variables that will be used inside of Retry
var resp *client.Response
var err error
// Retry a network request and return an error to signify to try again
err = expBackoff.Retry(func() error {
client := client.New()
resp, err = client.Get("https://gofiber.io")
if err != nil {
return fmt.Errorf("GET gofiber.io failed: %w", err)
}
if resp.StatusCode() != 200 {
return fmt.Errorf("GET gofiber.io did not return OK 200")
}
return nil
})
// If all retries failed, panic
if err != nil {
panic(err)
}
fmt.Printf("GET gofiber.io succeeded with status code %d\n", resp.StatusCode())
}Default Config
retry.NewExponentialBackoff()Custom Config
retry.NewExponentialBackoff(retry.Config{
InitialInterval: 2 * time.Second,
MaxBackoffTime: 64 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 15,
})Config
// Config defines the config for addon.
type Config struct {
// InitialInterval defines the initial time interval for backoff algorithm.
//
// Optional. Default: 1 * time.Second
InitialInterval time.Duration
// MaxBackoffTime defines maximum time duration for backoff algorithm. When
// the algorithm is reached this time, rest of the retries will be maximum
// 32 seconds.
//
// Optional. Default: 32 * time.Second
MaxBackoffTime time.Duration
// Multiplier defines multiplier number of the backoff algorithm.
//
// Optional. Default: 2.0
Multiplier float64
// MaxRetryCount defines maximum retry count for the backoff algorithm.
//
// Optional. Default: 10
MaxRetryCount int
}Default Config Example
// DefaultConfig is the default config for retry.
var DefaultConfig = Config{
InitialInterval: 1 * time.Second,
MaxBackoffTime: 32 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 10,
currentInterval: 1 * time.Second,
}Every Retry call starts over at InitialInterval and keeps its backoff state to itself, so one ExponentialBackoff can be shared by concurrent callers.
File: binder/README.md
Fiber Binders
Binder is a new request/response binding feature for Fiber introduced in Fiber v3. It replaces the old Fiber parsers and offers enhanced capabilities such as custom binder registration, struct validation, support for map[string]string, map[string][]string, and more. Binder replaces the following components:
BodyParserParamsParserGetReqHeadersGetRespHeadersAllParamsQueryParserReqHeaderParser
Default Binders
Fiber provides several default binders out of the box:
Guides
Binding into a Struct
Fiber supports binding request data directly into a struct using gofiber/schema. Here's an example:
// Field names must start with an uppercase letter
type Person struct {
Name string `json:"name" xml:"name" form:"name"`
Pass string `json:"pass" xml:"pass" form:"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) // Output: john
log.Println(p.Pass) // Output: doe
// Additional logic...
})
// Run tests with the following curl commands:
// JSON
curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000
// XML
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name>doe</login>" localhost:3000
// URL-Encoded Form
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
// Query Parameters
curl -X POST "http://localhost:3000/?name=john&pass=doe"Binding into a Map
Fiber allows binding request data into a map[string]string or map[string][]string. Here's an example:
app.Get("/", func(c fiber.Ctx) error {
params := make(map[string][]string)
if err := c.Bind().Query(params); err != nil {
return err
}
log.Println(params["name"]) // Output: [john]
log.Println(params["pass"]) // Output: [doe]
log.Println(params["products"]) // Output: [shoe hat]
// Additional logic...
return nil
})
// Run tests with the following curl command:
curl "http://localhost:3000/?name=john&pass=doe&products=shoe&products=hat"Automatic Error Handling with `WithAutoHandling`
By default, Fiber returns binder errors directly. To handle errors automatically and return a 400 Bad Request status, use the WithAutoHandling() method.
Example:
// Field names must start with an uppercase letter
type Person struct {
Name string `json:"name,required"`
Pass string `json:"pass"`
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().WithAutoHandling().JSON(p); err != nil {
return err
// Automatically returns status code 400
// Response: Bad request: name is empty
}
// Additional logic...
return nil
})
// Run tests with the following curl command:
curl -X GET -H "Content-Type: application/json" --data "{\"pass\":\"doe\"}" localhost:3000Defining a Custom Binder
Fiber maintains a minimal codebase by not including every possible binder. If you need to use a custom binder, you can easily register and utilize it. Here's an example of creating a toml binder.
type Person struct {
Name string `toml:"name"`
Pass string `toml:"pass"`
}
type tomlBinding struct{}
func (b *tomlBinding) Name() string {
return "toml"
}
func (b *tomlBinding) MIMETypes() []string {
return []string{"application/toml"}
}
func (b *tomlBinding) Parse(c fiber.Ctx, out any) error {
return toml.Unmarshal(c.Body(), out)
}
func main() {
app := fiber.New()
app.RegisterCustomBinder(&tomlBinding{})
app.Get("/", func(c fiber.Ctx) error {
out := new(Person)
if err := c.Bind().Body(out); err != nil {
return err
}
// Alternatively, specify the custom binder:
// if err := c.Bind().Custom("toml", out); err != nil {
// return err
// }
return c.SendString(out.Pass) // Output: test
})
app.Listen(":3000")
}
// Run tests with the following curl command:
curl -X GET -H "Content-Type: application/toml" --data "name = 'bar'
pass = 'test'" localhost:3000Defining a Custom Validator
All Fiber binders support struct validation if a validator is defined in the configuration. You can create your own validator or use existing ones like go-playground/validator or go-ozzo/ozzo-validation. Here's an example of a simple custom validator:
type Query struct {
Name string `query:"name"`
}
type structValidator struct{}
func (v *structValidator) Engine() any {
return nil // Implement if using an external validation engine
}
func (v *structValidator) ValidateStruct(out any) error {
data := reflect.ValueOf(out).Elem().Interface()
query := data.(Query)
if query.Name != "john" {
return errors.New("you should have entered the correct name!")
}
return nil
}
func main() {
app := fiber.New(fiber.Config{
StructValidator: &structValidator{},
})
app.Get("/", func(c fiber.Ctx) error {
out := new(Query)
if err := c.Bind().Query(out); err != nil {
return err // Returns: you should have entered the correct name!
}
return c.SendString(out.Name)
})
app.Listen(":3000")
}
// Run tests with the following curl command:
curl "http://localhost:3000/?name=efe"File: client/README.md
Fiber Client
Features
The characteristics have not yet been written.
- GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS, etc.
- Simple and chainable methods for settings and request
- Request Body can be
string,[]byte,map,slice- Auto detects
Content-Type - Buffer processing for
files - Native
*fasthttp.Requestinstance can be accessed during middleware and request execution viaRequest.RawRequest - Request Body can be read multiple time via
Request.RawRequest.GetBody()
- Auto detects
- Response object gives you more possibility
- Access as
[]bytebyresponse.Body()or access asstringbyresponse.String()
- Access as
- Automatic marshal and unmarshal for JSON and XML content type
- Default is JSON, if you supply struct/map without header Content-Type
- For auto-unmarshal, refer to -
- Success scenario Request.SetResult() and Response.Result().
- Error scenario Request.SetError() and Response.Error().
- Supports RFC7807 - application/problem+json & application/problem+xml
- Provide an option to override JSON Marshal/Unmarshal and XML Marshal/Unmarshal
Usage
The following samples will assist you to become as comfortable as possible with Fiber Client library.
// Import Fiber Client into your code and refer it as `client`.
import "github.com/gofiber/fiber/client"Simple GET
File: docs/guide/error-handling.md
id: error-handling title: 🐛 Error Handling Fiber supports centralized error handling: handlers return errors so you can log them or send a custom HTTP response to the client. sidebar_position: 4
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Catching Errors
Return errors from route handlers and middleware so Fiber can handle them centrally.
app.Get("/", func(c fiber.Ctx) error {
// Pass error to Fiber
return c.SendFile("file-does-not-exist")
})Fiber does not recover from panics by default. Add the Recover middleware to catch panics in any handler:
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/recover"
)
func main() {
app := fiber.New()
app.Use(recover.New())
app.Get("/", func(c fiber.Ctx) error {
panic("This panic is caught by fiber")
})
log.Fatal(app.Listen(":3000"))
}Use fiber.NewError() to create an error with a status code. If you omit the message, Fiber uses the standard status text (for example, 404 becomes Not Found).
app.Get("/", func(c fiber.Ctx) error {
// 503 Service Unavailable
return fiber.ErrServiceUnavailable
// 503 On vacation!
return fiber.NewError(fiber.StatusServiceUnavailable, "On vacation!")
})Default Error Handler
Fiber ships with a default error handler that sends 500 Internal Server Error for generic errors. If the error is a fiber.Error, the response uses the embedded status code and message.
// Default error handler
var DefaultErrorHandler = func(c fiber.Ctx, err error) error {
// Status code defaults to 500
code := fiber.StatusInternalServerError
var e *fiber.Error
matched := errors.As(err, &e)
if matched && e != nil {
code = e.Code
}
message := http.StatusText(code)
if err != nil && !(matched && e == nil) {
message = err.Error()
}
// Set Content-Type: text/plain; charset=utf-8
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
// Return status code with error message
return c.Status(code).SendString(message)
}Custom Error Handler
Set a custom error handler in fiber.Config when creating a new app.
The default handler covers most cases, but a custom handler lets you react to specific error types—for example, by logging to a service or sending a tailored JSON or HTML response.
The following example shows how to display error pages for different types of errors.
// Create a new fiber instance with custom config
app := fiber.New(fiber.Config{
// Override default error handler
ErrorHandler: func(ctx fiber.Ctx, err error) error {
// Status code defaults to 500
code := fiber.StatusInternalServerError
// Retrieve the custom status code if it's a *fiber.Error
var e *fiber.Error
if errors.As(err, &e) && e != nil {
code = e.Code
}
// Send custom error page
err = ctx.Status(code).SendFile(fmt.Sprintf("./%d.html", code))
if err != nil {
// In case the SendFile fails
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
}
// Return from handler
return nil
},
})
// ...Special thanks to the Echo and Express frameworks for inspiring parts of this error-handling approach.
File: docs/guide/faster-fiber.md
id: faster-fiber title: ⚡ Make Fiber Faster sidebar_position: 11
Custom JSON Encoder/Decoder
Fiber defaults to the standard encoding/json for stability and reliability. If you need more speed, consider these libraries:
package main
import "github.com/gofiber/fiber/v3"
import "github.com/goccy/go-json"
func main() {
app := fiber.New(fiber.Config{
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})
// ...
}References
- Set custom JSON encoder for client
- Set custom JSON decoder for client
- Set custom JSON encoder for application
- Set custom JSON decoder for application
Alternative Regex Engines for `regex()` Constraints
Fiber route patterns still do not support general regex routes, but you can swap
the compiler used by regex() parameter constraints throughConfig.RegexHandler. This lets you try
high-performance engines such as
coregex on the matching path.
Configure `RegexHandler`
Set RegexHandler to the compile function you want Fiber to use for regex()
constraints.
package main
import (
"github.com/coregx/coregex"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New(fiber.Config{
RegexHandler: coregex.MustCompile,
})
app.Get("/api/:id<regex(\\d+)>", func(c fiber.Ctx) error {
return c.SendString("ID: " + c.Params("id"))
})
}You can also set it explicitly to the standard library default:
package main
import (
"regexp"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New(fiber.Config{
RegexHandler: regexp.MustCompile,
})
_ = app
}Notes
RegexHandleronly affectsregex()parameter constraints- invalid patterns still panic during route registration because Fiber uses
MustCompile-style semantics - Fiber may invoke
RegexHandlermore than once per route while parsing raw and
normalized route patterns during registration - compiled matchers are reused across requests, so custom matchers must be safe
for concurrent use
Related Performance Reading
- Zero Allocation: why
Ctxvalues are reused across requests and how to copy them safely - Benchmarks: how Fiber compares to other frameworks
File: docs/guide/grouping.md
id: grouping title: 🎭 Grouping sidebar_position: 2
:::info
Grouping works like Express.js. Groups are virtual; routes are flattened with the group's prefix and executed in declaration order, mirroring Express.js.
:::
Paths
Groups can use path prefixes to organize related routes.
func main() {
app := fiber.New()
api := app.Group("/api", middleware) // /api
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
}:::note
Group prefixes follow the same slash-boundary rule as app.Use. A prefix must either match the full path or stop at a /, so /api applies to /api and /api/v1 but not /apiv1. Parameter markers (for example :id, :id?, *, and +) are processed before checking the boundary.
:::
Groups can also include an optional handler.
func main() {
app := fiber.New()
api := app.Group("/api") // /api
v1 := api.Group("/v1") // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2") // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
}:::caution
Accessing /api, /v1, or /v2 directly returns a 404, so add error handlers as needed.
:::
Group Handlers
Group handlers can act as routing paths but must call Next to continue the flow.
func main() {
app := fiber.New()
handler := func(c fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
}
api := app.Group("/api") // /api
v1 := api.Group("/v1", func(c fiber.Ctx) error { // middleware for /api/v1
c.Set("Version", "v1")
return c.Next()
})
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
log.Fatal(app.Listen(":3000"))
}Route
Route groups routes under a common prefix declared inside a single callback, with an optional name prefix. It is shorthand for nesting with Group.
app.Route("/api/v1", func(r fiber.Router) {
r.Get("/users", handler).Name("users") // /api/v1/users (name: v1.users)
r.Post("/users", handler).Name("create") // /api/v1/users (name: v1.create)
}, "v1.")RouteChain
When several HTTP methods share the same path, RouteChain lets you declare the path once and chain the verb handlers. An All in the chain runs before the verb handlers on that path, acting as route-specific middleware.
app.RouteChain("/events").
All(func(c fiber.Ctx) error { return c.Next() }). // route-local middleware
Get(func(c fiber.Ctx) error { return c.SendString("GET /events") }).
Post(func(c fiber.Ctx) error { return c.SendString("POST /events") }):::note
Within a chain, All registers prefix-matched middleware (like app.Use), not the exact-path App.All, so it also runs for sub-paths of the chain path.
:::
File: docs/guide/routing.md
id: routing title: 🔌 Routing Routing refers to how an application's endpoints (URIs) respond to client requests. sidebar_position: 1 toc_max_heading_level: 4
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import RoutingHandler from './../partials/routing/handler.md';
import RoutingUse from './../partials/routing/use.md';
import RoutingHandlerTypes from './../partials/routing/handler-types.md';
import RouteAnatomy from '@site/src/components/route-anatomy';
import MiddlewareVisualizer from '@site/src/components/middleware-visualizer';
Anatomy of a route
A route ties together an HTTP method, a path, and one or more handlers. Hover or click any colored part to jump to the section that explains it:
Get is the routing method, "/users/:id" is the route path (the resource, in REST terms) with :id a route parameter, and func(c fiber.Ctx) error is the handler (or middleware) run when the route matches.
Want to see matching happen? Fire requests at your own route table in the interactive Route Matcher.
Route Handlers
Here is a complete, runnable app for context:
package main
import "github.com/gofiber/fiber/v3"
func main() {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
}In the shorter examples throughout this guide, app is the *fiber.App returned by fiber.New(), and handler/middleware stand in for any func(c fiber.Ctx) error. Snippets that call fmt.Println or fmt.Fprintf also need import "fmt".
Beyond the native func(fiber.Ctx) forms, Fiber also adapts Express-style, net/http, and fasthttp handlers. See Handler types at the end of this guide for the full list of supported shapes.
Get vs Use vs All
Get (and the other method helpers like Post and Put) match a single HTTP method at an exact path. All matches an exact path across every HTTP method. Use registers middleware that matches by prefix and runs in declaration order, calling c.Next() to continue the chain.
app.Get("/users", func(c fiber.Ctx) error {
return c.SendString("GET /users")
})
// GET /users -> "GET /users"
// POST /users -> 405 Method Not Allowed
// GET /users/42 -> 404 Not Found (exact match only)app.All("/ping", func(c fiber.Ctx) error {
return c.SendString(c.Method() + " /ping")
})
// GET /ping -> "GET /ping"
// POST /ping -> "POST /ping"
// DELETE /ping -> "DELETE /ping"
// GET /ping/extra -> 404 Not Found (still exact path)// Empty Use: no path -> matches every request, any method, any path
app.Use(func(c fiber.Ctx) error {
c.Set("X-Powered-By", "Fiber")
return c.Next()
})
// Prefixed Use: matches the prefix and anything below a slash boundary
app.Use("/api", func(c fiber.Ctx) error {
return c.Next()
})
// The empty Use above runs for ALL of these. The notes below show which
// requests ALSO match the prefixed "/api" Use:
// /api -> also matches "/api" Use (exact prefix)
// /api/users -> also matches "/api" Use (slash boundary)
// /apiv2 -> empty Use only (no slash boundary)
// /anything -> empty Use onlyMultiple handlers that match the same request run in the order you declare them. Each must call c.Next() to pass control to the next; if one returns without calling it, the rest of the chain is skipped.
app.Use("/api", func(c fiber.Ctx) error {
fmt.Println("1: auth check")
return c.Next()
})
app.Use("/api", func(c fiber.Ctx) error {
fmt.Println("2: logging")
return c.Next()
})
app.Get("/api/users", func(c fiber.Ctx) error {
fmt.Println("3: handler")
return c.SendString("users")
})
// GET /api/users prints, in order:
// 1: auth check
// 2: logging
// 3: handlerAttach several handlers in a single registration: list the route-specific middleware before the business handler.
app.Get("/users/:id",
func(c fiber.Ctx) error { // 1: require authentication
if c.Get("Authorization") == "" {
return c.SendStatus(fiber.StatusUnauthorized) // returns without c.Next(): stops here
}
return c.Next()
},
func(c fiber.Ctx) error { // 2: stash data for downstream handlers
c.Locals("userID", c.Params("id"))
return c.Next()
},
func(c fiber.Ctx) error { // 3: business handler reads the stashed value
return c.SendString("user " + c.Locals("userID").(string))
},
)
// GET /users/42 (no Authorization header) -> 401, handlers 2 and 3 never run
// GET /users/42 (with Authorization) -> "user 42"| Helper | Methods matched | Path matching | Typical use |
|---|---|---|---|
Get/Post/… |
one | exact | a specific endpoint |
All |
every method | exact | one path, any verb |
Use |
every method | prefix (slash boundary); all paths if none given | middleware, mounting sub-apps |
A path that exists only for a different method returns 405 Method Not Allowed; a path that matches no route at all (including one rejected by a constraint) returns 404 Not Found.
Paths
A route path paired with an HTTP method defines an endpoint. It can be a plain string or a pattern.
// This route path will match requests to the root route, "/":
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("root")
})
// This route path will match requests to "/about":
app.Get("/about", func(c fiber.Ctx) error {
return c.SendString("about")
})
// This route path will match requests to "/random.txt":
app.Get("/random.txt", func(c fiber.Ctx) error {
return c.SendString("random.txt")
})The order in which you declare routes matters: like Express.js, routes are matched in registration order (first match wins), so declare more specific paths before those that contain parameters. Note that method helpers such as Get match the exact path only.
:::info
Place routes with variable parameters after fixed paths to avoid unintended matches.
:::
Path normalization
Before a request is matched, Fiber normalizes its path as RFC 3986 Section 6.2.2 describes:
- Percent-encoded unreserved characters (letters, digits,
-,.,_and~) are decoded, so/%70rivateis matched as/private. Every other escape stays encoded with uppercase hex digits, so an encoded slash (%2F) never splits a segment and/a%2fbis matched as/a%2Fb. WithUnescapePathenabled, every escape is decoded instead. .and..segments are resolved, so/static/x/../privateand/static/./privateare both matched as/static/private. A..never climbs above the root. Empty segments are kept:/static//privateis a different path from/static/private.
Middleware mounted on a prefix therefore sees these spellings of a path under it, and c.Path() returns the normalized path. Decoding happens exactly once: /%2570rivate is matched as /%2570rivate by default and as /%70rivate with UnescapePath, and neither becomes /private. c.OriginalURL() still returns the request target as the client sent it.
Parameters
Route parameters are dynamic segments in a path, either named or unnamed, used to capture values from the URL. Retrieve them with the Params function using the parameter name or, for unnamed parameters, the wildcard (*) or plus (+) symbol with an index.
The characters :, +, and * introduce parameters. Append ? to a named segment to make it optional. + is a greedy, required wildcard (it must match at least one character); * is a greedy, optional wildcard (it can match nothing).
// Named parameters
app.Get("/user/:name/books/:title", func(c fiber.Ctx) error {
fmt.Fprintf(c, "%s\n", c.Params("name"))
fmt.Fprintf(c, "%s\n", c.Params("title"))
return nil
})
// Plus - greedy, required (matches at least one character)
app.Get("/user/+", func(c fiber.Ctx) error {
return c.SendString(c.Params("+"))
})
// Optional named parameter
app.Get("/user/:name?", func(c fiber.Ctx) error {
return c.SendString(c.Params("name"))
})
// Wildcard - greedy, optional (may match nothing)
app.Get("/user/*", func(c fiber.Ctx) error {
return c.SendString(c.Params("*"))
})The hyphen (-), dot (.), and colon (:) are treated literally between parameters, so you can combine them with route parameters. Fiber's router detects when these characters belong to the literal path.
// http://localhost:3000/plantae/prunus.persica
app.Get("/plantae/:genus.:species", func(c fiber.Ctx) error {
fmt.Fprintf(c, "%s.%s\n", c.Params("genus"), c.Params("species"))
return nil // prunus.persica
})
// http://localhost:3000/flights/LAX-SFO
app.Get("/flights/:from-:to", func(c fiber.Ctx) error {
fmt.Fprintf(c, "%s-%s\n", c.Params("from"), c.Params("to"))
return nil // LAX-SFO
})
// http://localhost:3000/shop/product/color:blue/size:xs
app.Get("/shop/product/color::color/size::size", func(c fiber.Ctx) error {
fmt.Fprintf(c, "%s:%s\n", c.Params("color"), c.Params("size"))
return nil // blue:xs
})Escape special parameter characters with \\ to treat them literally. This is useful for custom methods like those in the Google API Design Guide. Wrap routes in backticks to keep escape sequences clear.
// Matches "/v1/some/resource/name:customVerb" because the colon is escaped
app.Get(`/v1/some/resource/name\:customVerb`, func(c fiber.Ctx) error {
return c.SendString("Hello, Community")
})
// Matches only the literal path "/*"; an escaped star is not the catch-all route
app.Get(`/\*`, func(c fiber.Ctx) error {
return c.SendString("literal star")
})You can chain multiple named or unnamed parameters, including wildcard and plus segments, within a single segment.
// GET /@v1
// Params: "sign" -> "@", "param" -> "v1"
app.Get("/:sign:param", handler)
// GET /api-v1
// Params: "name" -> "v1"
app.Get("/api-:name", handler)
// GET /customer/v1/cart/proxy
// Params: "*1" -> "customer/", "*2" -> "/cart"
app.Get("/*v1*/proxy", handler)
// GET /v1/brand/4/shop/blue/xs
// Params: "*1" -> "brand/4", "*2" -> "blue/xs"
app.Get("/v1/*/shop/*", handler):::info
Fiber lets multiple parameters share a single path segment, unlike routers such as Express, Gin, and Echo where :param always consumes a whole segment. When named parameters are adjacent with no literal separator between them, the router cannot know where one parameter ends and the next begins: there is no delimiter in the pattern to split on.
To keep matching deterministic, Fiber assigns every adjacent named parameter except the last a fixed length of one character, and the last parameter takes the remainder of the segment (up to the next delimiter or end of path). This is why /:sign:param against /@v1 yields sign=@ and param=v1, not sign=@v and param=1.
If you need a multi-character split, put a literal delimiter between parameters, for example /:sign-:param matching /@v-1.
:::
When a route has several wildcard (*) or plus (+) segments, retrieve them positionally with a 1-based index matching the symbol: c.Params("*1") and c.Params("*2") for wildcards, c.Params("+1") and c.Params("+2") for plus segments. A single wildcard or plus is just c.Params("*") or c.Params("+").
Fiber's routing is inspired by Express but intentionally omits regex route patterns due to their performance cost. To validate a parameter against a regular expression, use the regex() constraint described below.
Constraints
Route constraints execute when a match has occurred to the incoming URL and the URL path is tokenized into route values by parameters. The feature was introduced in v2.37.0 and inspired by .NET Core.
:::caution
Constraints are matching rules, not input validation: if a value fails a constraint, the route simply does not match and Fiber returns 404 Not Found.
:::
| Constraint | Example | Example matches |
|---|---|---|
| int | :id |
123456789, -123456789 |
| bool | :active |
true,false |
| guid | :id<guid> |
CD2C1638-1638-72D5-1638-DEADBEEF1638 |
| float | :weight<float> |
1.234, -1001.01e8, 3.14 |
| minLen(value) | :username<minLen(4)> |
Test (must be at least 4 characters) |
| maxLen(value) | :filename<maxLen(8)> |
MyFile (must be no more than 8 characters) |
| len(length) | :filename<len(12)> |
somefile.txt (exactly 12 characters) |
| min(value) | :age<min(18)> |
19 (Integer value must be at least 18) |
| max(value) | :age<max(120)> |
91 (Integer value must be no more than 120) |
| range(min,max) | :age<range(18,120)> |
91 (Integer value must be at least 18 but no more than 120) |
| alpha | :name<alpha> |
Rick (String must consist of one or more alphabetical characters, a-z and case-insensitive) |
| datetime | :dob<datetime(2006\\-01\\-02)> |
2005-11-01 |
| regex(expression) | :date<regex(\d{4}-\d{2}-\d{2})> |
2022-08-27 (Must match regular expression) |
Constraint names and arguments are always taken from the pattern as written, even with the default case-insensitive routing: :code<regex([A-Z]{2})> keeps matching uppercase codes only, a datetime layout keeps its T and Z, and a custom constraint registered under a mixed-case name is found.
Examples
app.Get("/:test<min(5)>", func(c fiber.Ctx) error {
return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/12
// 12
// curl -X GET http://localhost:3000/1
// Not FoundYou can use ; for multiple constraints.
app.Get("/:test<min(100);maxLen(5)>", func(c fiber.Ctx) error {
return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/120000
// Not Found
// curl -X GET http://localhost:3000/1
// Not Found
// curl -X GET http://localhost:3000/250
// 250Fiber precompiles the regex when registering routes, so the pattern is matched (not recompiled) on each request.
app.Get(`/:date<regex(\d{4}-\d{2}-\d{2})>`, func(c fiber.Ctx) error {
return c.SendString(c.Params("date"))
})
// curl -X GET http://localhost:3000/125
// Not Found
// curl -X GET http://localhost:3000/test
// Not Found
// curl -X GET http://localhost:3000/2022-08-27
// 2022-08-27:::caution
When using the datetime constraint, prefix routing characters (*, +, ?, :, /, <, >, ;, (, )) with \\ to avoid misparsing.
:::
Optional Parameter Example
You can impose constraints on optional parameters as well.
app.Get("/:test?", func(c fiber.Ctx) error {
return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/42
// 42
// curl -X GET http://localhost:3000/
//
// curl -X GET http://localhost:3000/7.0
// Not FoundCustom Constraint
Custom constraints can be added to Fiber using the app.RegisterCustomConstraint method. Your constraints have to be compatible with the CustomConstraint interface.
:::caution
Attention, custom constraints can now override built-in constraints. If a custom constraint has the same name as a built-in constraint, the custom constraint will be used instead. This allows for more flexibility in defining route parameter constraints.
:::
Add external constraints when you need stricter rules, such as verifying that a parameter is a valid ULID.
// CustomConstraint is an interface for custom constraints
type CustomConstraint interface {
// Name returns the name of the constraint.
// This name is used in the constraint matching.
Name() string
// Execute executes the constraint.
// It returns true if the constraint is matched and right.
// param is the parameter value to check.
// args are the constraint arguments.
Execute(param string, args ...string) bool
}You can check the example below:
type UlidConstraint struct {
fiber.CustomConstraint
}
func (*UlidConstraint) Name() string {
return "ulid"
}
func (*UlidConstraint) Execute(param string, args ...string) bool {
_, err := ulid.Parse(param)
return err == nil
}
func main() {
app := fiber.New()
app.RegisterCustomConstraint(&UlidConstraint{})
app.Get("/login/:id", func(c fiber.Ctx) error {
return c.SendString("...")
})
app.Listen(":3000")
// /login/01HK7H9ZE5BFMK348CPYP14S0Z -> 200
// /login/12345 -> 404
}ConstraintHandler Interface
In addition to the CustomConstraint interface, Fiber v3 provides a more powerful ConstraintHandler interface that supports precomputation at route registration time. All built-in constraints implement this interface.
// ConstraintHandler is the interface that all constraints must implement.
type ConstraintHandler interface {
// Name returns the constraint identifier used in route patterns.
Name() string
// Execute validates a request parameter value against the constraint.
// data contains the pre-typed constraint data produced by Analyze().
Execute(param string, data []any) bool
}Optionally, a constraint can implement ConstraintAnalyzer to preprocess data at registration time, avoiding repeated parsing on every request:
// ConstraintAnalyzer is an optional interface for registration-time precomputation.
type ConstraintAnalyzer interface {
// Analyze preprocesses constraint data at route registration time.
// Returns pre-typed values that will be stored in Constraint.Data.
Analyze(args []string) ([]any, error)
}:::note
Existing CustomConstraint implementations continue to work unchanged. They are automatically wrapped to satisfy ConstraintHandler. Custom constraints that also implement ConstraintAnalyzer will have their Analyze method called at registration time.
:::
:::tip Try it live
Test route patterns, registration order, and constraints against real requests in the interactive Route Matcher tool.
:::
Middleware
Functions that are designed to make changes to the request or response are called middleware functions. c.Next() passes control to the next handler in the matched chain (middleware or route handler); if a handler returns without calling it, the remaining handlers are skipped.
app.Use(func(c fiber.Ctx) error {
// Set a custom header on all responses:
c.Set("X-Custom-Header", "Hello, World")
// Go to next middleware:
return c.Next()
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})Once a route matches, its handlers run as one chain in registration order; each handler decides with c.Next() whether the rest of the chain runs. Step through the chain yourself, including what happens when a middleware short-circuits instead of calling c.Next():
See Get vs Use vs All for how Use prefix matching differs from exact route matching, and how multiple handlers run in order.
Use
Adding or removing routes at runtime
:::caution
Defining all routes before the app starts is strongly recommended. You can still change them at runtime with RebuildTree, RemoveRoute, RemoveRouteByName, and RemoveRouteFunc, but these operations are not thread-safe and are performance-intensive, so use them sparingly and only in development.
:::
Grouping
If you have many endpoints, you can organize your routes using Group.
func main() {
app := fiber.New()
api := app.Group("/api", middleware) // /api
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
}More information about this in our Grouping Guide.
Route
Route is shorthand for Group: it scopes a set of routes under a common prefix declared inside a single callback, with an optional name prefix.
app.Route("/api/v1", func(r fiber.Router) {
r.Get("/users", handler).Name("users") // /api/v1/users (name: v1.users)
r.Post("/users", handler).Name("create") // /api/v1/users (name: v1.create)
}, "v1.")RouteChain
When several HTTP methods share the same path, RouteChain lets you declare the path once and chain the verb handlers. An All in the chain runs before the verb handlers on that path, acting as route-specific middleware.
app.RouteChain("/events").
All(func(c fiber.Ctx) error { return c.Next() }). // route-local middleware
Get(func(c fiber.Ctx) error { return c.SendString("GET /events") }).
Post(func(c fiber.Ctx) error { return c.SendString("POST /events") }):::note
Within a chain, All registers prefix-matched middleware (like Use), not the exact-path App.All, so it also runs for sub-paths of the chain path.
:::
Pick the helper that fits: a single endpoint uses Get/Post/…; a fixed set of methods on one path uses Add; one path with many methods (fluently) uses RouteChain; many paths under a shared prefix use Group or Route.
Automatic HEAD routes
Fiber automatically registers a HEAD route for every GET route you add. The generated handler chain mirrors the GET chain, so HEAD requests reuse middleware, status codes, and headers while the response body is suppressed. This covers the routes of mounted sub-apps at any nesting depth, and routes added at runtime get their HEAD companion when RebuildTree publishes them.
app := fiber.New()
app.Get("/users/:id", func(c fiber.Ctx) error {
c.Set("X-User", c.Params("id"))
return c.SendStatus(fiber.StatusOK)
})
// HEAD /users/:id now returns the same headers and status without a body.You can still register dedicated HEAD handlers, even with auto-registration enabled, and Fiber replaces the generated route so your implementation wins:
app.Head("/users/:id", func(c fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
})To opt out globally, start the app with DisableHeadAutoRegister:
handler := func(c fiber.Ctx) error {
c.Set("X-User", c.Params("id"))
return c.SendStatus(fiber.StatusOK)
}
app := fiber.New(fiber.Config{DisableHeadAutoRegister: true})
app.Get("/users/:id", handler) // HEAD /users/:id now returns 405 unless you add it manually.Auto-generated HEAD routes participate in every router scope, including Group hierarchies, mounted sub-apps, parameterized and wildcard paths, and static file helpers. They also appear in route listings such as app.Stack() so tooling sees both the GET and HEAD entries.
Handler types
File: docs/guide/utils.md
id: utils title: 🧰 Utils sidebar_position: 9 toc_max_heading_level: 4
Generics
Convert
Converts a string to a specific type while handling errors and optional defaults.
It wraps conversion and fallback logic to keep your code clean and consistent.
func Convert[T any](value string, converter func(string) (T, error), defaultValue ...T) (T, error)// GET http://example.com/id/bb70ab33-d455-4a03-8d78-d3c1dacae9ff
app.Get("/id/:id", func(c fiber.Ctx) error {
fiber.Convert(c.Params("id"), uuid.Parse) // UUID(bb70ab33-d455-4a03-8d78-d3c1dacae9ff), nil
})
// GET http://example.com/search?id=65f6f54221fb90e6a6b76db7
app.Get("/search", func(c fiber.Ctx) error {
fiber.Convert(c.Query("id"), mongo.ParseObjectID) // objectid(65f6f54221fb90e6a6b76db7), nil
fiber.Convert(c.Query("id"), uuid.Parse) // uuid.Nil, error(cannot parse given uuid)
fiber.Convert(c.Query("id"), uuid.Parse, mongo.NewObjectID) // new object id generated and return nil as error.
return nil
})
// ...GetReqHeader
Retrieves an HTTP request header as a specific type using generics.
func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) Vapp.Get("/search", func(c fiber.Ctx) error {
// curl -X GET http://example.com/search -H "X-Request-ID: 12345" -H "X-Request-Name: John"
fiber.GetReqHeader[int](c, "X-Request-ID") // => returns 12345 as integer.
fiber.GetReqHeader[string](c, "X-Request-Name") // => returns "John" as string.
fiber.GetReqHeader[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
})Locals
Reads or writes local values in the request context using generics.
// Set a value
func Locals[V any](c Ctx, key any, value ...V) V
// Get a value
func Locals[V any](c Ctx, key any) Vapp.Use("/user/:user/:id", func(c fiber.Ctx) error {
// set local values
fiber.Locals[string](c, "user", "john")
fiber.Locals[int](c, "id", 25)
// ...
return c.Next()
})
app.Get("/user/*", func(c fiber.Ctx) error {
// get local values
name := fiber.Locals[string](c, "user") // john
age := fiber.Locals[int](c, "id") // 25
// ...
})Params
Retrieves route parameters as a specific type.
func Params[V GenericType](c Ctx, key string, defaultValue ...V) Vapp.Get("/user/:user/:id", func(c fiber.Ctx) error {
// http://example.com/user/john/25
fiber.Params[int](c, "id") // => returns 25 as integer.
fiber.Params[int](c, "unknownParam", 99) // => returns the default 99 as integer.
// ...
return c.SendString("Hello, " + fiber.Params[string](c, "user"))
})Query
Retrieves query parameters as a specific type.
func Query[V GenericType](c Ctx, key string, defaultValue ...V) Vapp.Get("/search", func(c fiber.Ctx) error {
// http://example.com/search?name=john&age=25
fiber.Query[string](c, "name") // => returns "john"
fiber.Query[int](c, "age") // => returns 25 as integer.
fiber.Query[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
})RoutePatternMatch
Checks whether a given path matches a Fiber route pattern. Useful for testing
patterns without registering them. Patterns may contain parameters, wildcards
and optional segments. An optional Config allows control over case sensitivity
and strict routing.
The path is normalized exactly the way the router normalizes an incoming
request before matching, so the answer agrees with what the app would actually
do. In particular, with the default StrictRouting: false a trailing slash on
the path is ignored, so RoutePatternMatch("/a/", "/a") reports true. SetStrictRouting: true if you need the two forms to be distinguished.
func RoutePatternMatch(path, pattern string, cfg ...Config) boolfiber.RoutePatternMatch("/user/john", "/user/:name") // true
fiber.RoutePatternMatch(
"/User/john",
"/user/:name",
fiber.Config{CaseSensitive: true},
) // false2. Official Technical Reference & Guides (gofiber/docs)
File: README.md
Website
This website is built using Docusaurus 3, a modern static website generator.
Prerequisites
- Node.js
>= 20 - npm
>= 10
Installation
$ npm ciLocal Development
$ npm run startThis command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
Build
$ npm run buildThis command generates static content into the build directory and can be served using any static contents hosting service.
Build only docs site:
$ npm run build:docsBuild only homepage:
$ npm run build:homeRun setup checks (TypeScript + both build targets):
$ npm run checkDeployment
Using SSH:
$ USE_SSH=true npm run deployNot using SSH:
$ GIT_USER=<Your GitHub username> npm run deployIf you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the gh-pages branch.
File: contrib_versioned_docs/version-websocket_v1.x.x/paseto/README.md
id: paseto
Paseto
PASETO returns a Web Token (PASETO) auth middleware.
- For valid token, it sets the payload data in Ctx.Locals and calls next handler.
- For invalid token, it returns "401 - Unauthorized" error.
- For missing token, it returns "400 - BadRequest" error.
Note: Requires Go 1.18 and above
Install
This middleware supports Fiber v2.
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/paseto
go get -u github.com/o1egl/pasetoSignature
pasetoware.New(config ...pasetoware.Config) func(*fiber.Ctx) errorConfig
| Property | Type | Description | Default |
|---|---|---|---|
| Next | func(*Ctx) bool |
Defines a function to skip middleware | nil |
| SuccessHandler | func(*fiber.Ctx) error |
SuccessHandler defines a function which is executed for a valid token. | c.Next() |
| ErrorHandler | func(*fiber.Ctx, error) error |
ErrorHandler defines a function which is executed for an invalid token. | 401 Invalid or expired PASETO |
| Validate | PayloadValidator |
Defines a function to validate if payload is valid. Optional. In case payload used is created using CreateToken function. If token is created using another function, this function must be provided. |
nil |
| SymmetricKey | []byte |
Secret key to encrypt token. If present the middleware will generate local tokens. | nil |
| PrivateKey | ed25519.PrivateKey |
Secret key to sign the tokens. If present (along with its PublicKey) the middleware will generate public tokens. |
nil |
| PublicKey | crypto.PublicKey |
Public key to verify the tokens. If present (along with PrivateKey) the middleware will generate public tokens. |
nil |
| ContextKey | string |
Context key to store user information from the token into context. | "auth-token" |
| TokenLookup | [2]string |
TokenLookup is a string slice with size 2, that is used to extract token from the request | ["header","Authorization"] |
Instructions
When using this middleware, and creating a token for authentication, you can use the function pasetoware.CreateToken,
that will create a token, encrypt or sign it and returns the PASETO token.
Passing a SymmetricKey in the Config results in a local (encrypted) token, while passing a PublicKey
and PrivateKey results in a public (signed) token.
In case you want to use your own data structure, is needed to provide the Validate function in paseware.Config, that
will return the data stored in the token, and a error.
Examples
Below have a list of some examples that can help you start to use this middleware. In case of any additional example
that doesn't show here, please take a look at the test file.
SymmetricKey
package main
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/o1egl/paseto"
pasetoware "github.com/gofiber/contrib/paseto"
)
const secretSymmetricKey = "symmetric-secret-key (size = 32)"
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
SymmetricKey: []byte(secretSymmetricKey),
TokenPrefix: "Bearer",
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create token and encrypt it
encryptedToken, err := pasetoware.CreateToken([]byte(secretSymmetricKey), user, 12*time.Hour, pasetoware.PurposeLocal)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(string)
return c.SendString("Welcome " + payload)
}Test it
Login using username and password to retrieve a token.
curl --data "user=john&pass=doe" http://localhost:8088/loginResponse
{
"token": "v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbAY"
}Request a restricted resource using the token in Authorization request header.
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbA"Response
Welcome johnSymmetricKey + Custom Validator callback
package main
import (
"encoding/json"
"time"
"github.com/o1egl/paseto"
pasetoware "github.com/gofiber/contrib/paseto"
)
const secretSymmetricKey = "symmetric-secret-key (size = 32)"
type customPayloadStruct struct {
Name string `json:"name"`
ExpiresAt time.Time `json:"expiresAt"`
}
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
SymmetricKey: []byte(secretSymmetricKey),
TokenPrefix: "Bearer",
Validate: func(decrypted []byte) (any, error) {
var payload customPayloadStruct
err := json.Unmarshal(decrypted, &payload)
return payload, err
},
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create the payload
payload := customPayloadStruct{
Name: "John Doe",
ExpiresAt: time.Now().Add(12 * time.Hour),
}
// Create token and encrypt it
encryptedToken, err := paseto.NewV2().Encrypt([]byte(secretSymmetricKey), payload, nil)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(customPayloadStruct)
return c.SendString("Welcome " + payload.Name)
}Test it
Login using username and password to retrieve a token.
curl --data "user=john&pass=doe" http://localhost:8088/loginResponse
{
"token": "v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA"
}Request a restricted resource using the token in Authorization request header.
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA"Response
Welcome John DoePublicPrivate Key
package main
import (
"crypto/ed25519"
"encoding/hex"
"time"
"github.com/gofiber/fiber/v2"
pasetoware "github.com/gofiber/contrib/paseto"
)
const privateKeySeed = "e9c67fe2433aa4110caf029eba70df2c822cad226b6300ead3dcae443ac3810f"
var seed, _ = hex.DecodeString(privateKeySeed)
var privateKey = ed25519.NewKeyFromSeed(seed)
type customPayloadStruct struct {
Name string `json:"name"`
ExpiresAt time.Time `json:"expiresAt"`
}
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
TokenPrefix: "Bearer",
PrivateKey: privateKey,
PublicKey: privateKey.Public(),
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create token and encrypt it
encryptedToken, err := pasetoware.CreateToken(privateKey, user, 12*time.Hour, pasetoware.PurposePublic)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(string)
return c.SendString("Welcome " + payload)
}Test it
Login using username and password to retrieve a token.
curl --data "user=john&pass=doe" http://localhost:8088/loginResponse
{
"token": "v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA"
}Request a restricted resource using the token in Authorization request header.
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA"Response
Welcome John DoeFile: contrib_versioned_docs/version-websocket_v1.x.x/otelfiber/example/README.md
id: otelfiber-example
Example
An HTTP server using gofiber fiber and instrumentation. The server has a/users/:id endpoint. The server generates span information tostdout.
These instructions expect you have
docker-compose installed.
Bring up the fiber-server and fiber-client services to run the
example:
docker-compose up --detach fiber-server fiber-clientThe fiber-client service sends just one HTTP request to fiber-server
and then exits. View the span generated by fiber-server in the logs:
docker-compose logs fiber-serverShut down the services when you are finished with the example:
docker-compose downFile: contrib_versioned_docs/version-websocket_v1.x.x/otelfiber/README.md
id: otelfiber
Otelfiber
OpenTelemetry support for Fiber.
Can be found on OpenTelemetry Registry.
Note: Requires Go 1.19 and above
Install
This middleware supports Fiber v2.
go get -u github.com/gofiber/contrib/otelfiber/v2Signature
otelfiber.Middleware(opts ...otelfiber.Option) fiber.HandlerConfig
You can configure the middleware using functional parameters
| Function | Argument Type | Description | Default |
|---|---|---|---|
WithNext |
func(*fiber.Ctx) bool |
Define a function to skip this middleware when returned true . | nil |
WithTracerProvider |
oteltrace.TracerProvider |
Specifies a tracer provider to use for creating a tracer. | nil - the global tracer provider is used |
WithMeterProvider |
otelmetric.MeterProvider |
Specifies a meter provider to use for reporting. | nil - the global meter provider is used |
WithPort |
int |
Specifies the value to use when setting the net.host.port attribute on metrics/spans. |
Defaults to (80 for http, 443 for https) |
WithPropagators |
propagation.TextMapPropagator |
Specifies propagators to use for extracting information from the HTTP requests. | If none are specified, global ones will be used |
WithServerName |
string |
Specifies the value to use when setting the http.server_name attribute on metrics/spans. |
- |
WithSpanNameFormatter |
func(*fiber.Ctx) string |
Takes a function that will be called on every request and the returned string will become the span Name. | Default formatter returns the route pathRaw |
WithCustomAttributes |
func(*fiber.Ctx) []attribute.KeyValue |
Define a function to add custom attributes to the span. | nil |
WithCustomMetricAttributes |
func(*fiber.Ctx) []attribute.KeyValue |
Define a function to add custom attributes to the metrics. | nil |
WithCollectClientIP |
bool |
Specifies whether to collect the client's IP address from the request. | true |
Usage
Please refer to example
Example
/* Detailed source-code truncated for AI context efficiency. */File: contrib_versioned_docs/version-websocket_v1.x.x/opafiber/README.md
id: opafiber
Opafiber
Open Policy Agent support for Fiber.
Note: Requires Go 1.19 and above
Install
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/opafiber/v2Signature
opafiber.New(config opafiber.Config) fiber.HandlerConfig
| Property | Type | Description | Default |
|---|---|---|---|
| RegoQuery | string |
Required - Rego query | - |
| RegoPolicy | io.Reader |
Required - Rego policy | - |
| IncludeQueryString | bool |
Include query string as input to rego policy | false |
| DeniedStatusCode | int |
Http status code to return when policy denies request | 400 |
| DeniedResponseMessage | string |
Http response body text to return when policy denies request | "" |
| IncludeHeaders | []string |
Include headers as input to rego policy | - |
| InputCreationMethod | InputCreationFunc |
Use your own function to provide input for OPA | func defaultInput(ctx *fiber.Ctx) (map[string]interface{}, error) |
Types
type InputCreationFunc func(c *fiber.Ctx) (map[string]interface{}, error)Usage
OPA Fiber middleware sends the following example data to the policy engine as input:
{
"method": "GET",
"path": "/somePath",
"query": {
"name": ["John Doe"]
},
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
}
}package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/contrib/opafiber/v2"
)
func main() {
app := fiber.New()
module := `
package example.authz
default allow := false
allow {
input.method == "GET"
}
`
cfg := opafiber.Config{
RegoQuery: "data.example.authz.allow",
RegoPolicy: bytes.NewBufferString(module),
IncludeQueryString: true,
DeniedStatusCode: fiber.StatusForbidden,
DeniedResponseMessage: "status forbidden",
IncludeHeaders: []string{"Authorization"},
InputCreationMethod: func (ctx *fiber.Ctx) (map[string]interface{}, error) {
return map[string]interface{}{
"method": ctx.Method(),
"path": ctx.Path(),
}, nil
},
}
app.Use(opafiber.New(cfg))
app.Get("/", func(ctx *fiber.Ctx) error {
return ctx.SendStatus(200)
})
app.Listen(":8080")
}File: contrib_versioned_docs/version-websocket_v1.x.x/monitor/README.md
id: monitor
Monitor
Monitor middleware for Fiber that reports server metrics, inspired by express-status-monitor
Install
This middleware supports Fiber v3.
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/monitorSignature
monitor.New(config ...monitor.Config) fiber.HandlerConfig
| Property | Type | Description | Default |
|---|---|---|---|
| Title | string |
Metrics page title. | Fiber Monitor |
| Refresh | time.Duration |
Refresh period. | 3 seconds |
| APIOnly | bool |
Whether the service should expose only the montioring API. | false |
| Next | func(c *fiber.Ctx) bool |
Define a function to add custom fields. | nil |
| CustomHead | string |
Custom HTML code to Head Section(Before End). | empty |
| FontURL | string |
FontURL for specilt font resource path or URL. also you can use relative path. | https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap |
| ChartJsURL | string |
ChartJsURL for specilt chartjs library, path or URL, also you can use relative path. | https://cdn.jsdelivr.net/npm/[email protected]/dist/Chart.bundle.min.js |
Example
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/contrib/monitor"
)
func main() {
app := fiber.New()
// Initialize default config (Assign the middleware to /metrics)
app.Get("/metrics", monitor.New())
// Or extend your config for customization
// Assign the middleware to /metrics
// and change the Title to `MyService Metrics Page`
app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"}))
log.Fatal(app.Listen(":3000"))
}Default Config
var ConfigDefault = Config{
Title: defaultTitle,
Refresh: defaultRefresh,
FontURL: defaultFontURL,
ChartJsURL: defaultChartJSURL,
CustomHead: defaultCustomHead,
APIOnly: false,
Next: nil,
}File: contrib_versioned_docs/version-websocket_v1.x.x/loadshed/README.md
id: loadshed
LoadShed
The LoadShed middleware for Fiber is designed to help manage server load by shedding requests based on certain load criteria.
Note: Requires Go 1.19 and above
Install
This middleware supports Fiber v2
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/loadshedSignatures
loadshed.New(config ...loadshed.Config) fiber.HandlerExamples
To use the LoadShed middleware in your Fiber application, import it and apply it to your Fiber app. Here's an example:
package main
import (
"github.com/gofiber/fiber/v2"
loadshed "github.com/gofiber/contrib/loadshed"
)
func main() {
app := fiber.New()
// Configure and use LoadShed middleware
app.Use(loadshed.New(loadshed.Config{
Criteria: &loadshed.CPULoadCriteria{
LowerThreshold: 0.75, // Set your own lower threshold
UpperThreshold: 0.90, // Set your own upper threshold
Interval: 10 * time.Second,
Getter: &loadshed.DefaultCPUPercentGetter{},
},
}))
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Welcome!")
})
app.Listen(":3000")
}Config
The LoadShed middleware in Fiber offers various configuration options to tailor the load shedding behavior according to the needs of your application.
| Property | Type | Description | Default |
|---|---|---|---|
| Next | func(*fiber.Ctx) bool |
Function to skip this middleware when returned true. | nil |
| Criteria | LoadCriteria |
Interface for defining load shedding criteria. | &CPULoadCriteria{...} |
LoadCriteria
LoadCriteria is an interface in the LoadShed middleware that defines the criteria for determining when to shed load in the system. Different implementations of this interface can use various metrics and algorithms to decide when and how to shed incoming requests to maintain system performance.
CPULoadCriteria
CPULoadCriteria is an implementation of the LoadCriteria interface, using CPU load as the metric for determining whether to shed requests.
Properties
| Property | Type | Description |
|---|---|---|
| LowerThreshold | float64 |
The lower CPU usage threshold as a fraction (0.0 to 1.0). Requests are considered for shedding when CPU usage exceeds this threshold. |
| UpperThreshold | float64 |
The upper CPU usage threshold as a fraction (0.0 to 1.0). All requests are shed when CPU usage exceeds this threshold. |
| Interval | time.Duration |
The time interval over which the CPU usage is averaged for decision making. |
| Getter | CPUPercentGetter |
Interface to retrieve CPU usage percentages. |
How It Works
CPULoadCriteria determines the load on the system based on CPU usage and decides whether to shed incoming requests. It operates on the following principles:
- CPU Usage Measurement: It measures the CPU usage over a specified interval.
- Thresholds: Utilizes
LowerThresholdandUpperThresholdvalues to decide when to start shedding requests. - Proportional Rejection Probability:
- Below
LowerThreshold: No requests are rejected, as the system is considered under acceptable load. - Between
LowerThresholdandUpperThreshold: The probability of rejecting a request increases as the CPU usage approaches theUpperThreshold. This is calculated using the formula:plaintextrejectionProbability := (cpuUsage - LowerThreshold*100) / (UpperThreshold - LowerThreshold) - Above
UpperThreshold: All requests are rejected to prevent system overload.
- Below
This mechanism ensures that the system can adaptively manage its load, maintaining stability and performance under varying traffic conditions.
Default Config
This is the default configuration for LoadCriteria in the LoadShed middleware.
var ConfigDefault = Config{
Next: nil,
Criteria: &CPULoadCriteria{
LowerThreshold: 0.90, // 90% CPU usage as the start point for considering shedding
UpperThreshold: 0.95, // 95% CPU usage as the point where all requests are shed
Interval: 10 * time.Second, // CPU usage is averaged over 10 seconds
Getter: &DefaultCPUPercentGetter{}, // Default method for getting CPU usage
},
}File: contrib_versioned_docs/version-websocket_v1.x.x/jwt/README.md
id: jwt
JWT
JWT returns a JSON Web Token (JWT) auth middleware.
For valid token, it sets the user in Ctx.Locals and calls next handler.
For invalid token, it returns "401 - Unauthorized" error.
For missing token, it returns "400 - Bad Request" error.
Special thanks and credits to Echo
Note: Requires Go 1.19 and above
Install
This middleware supports Fiber v1 & v2, install accordingly.
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/jwt
go get -u github.com/golang-jwt/jwt/v5Signature
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) errorConfig
| Property | Type | Description | Default |
|---|---|---|---|
| Filter | func(*fiber.Ctx) bool |
Function to skip middleware execution for specific requests. Optional. | nil |
| SuccessHandler | fiber.Handler |
Handler executed when a token is successfully validated. Optional. | nil |
| ErrorHandler | fiber.ErrorHandler |
Handler executed when token validation fails. Allows customization of JWT error responses. Optional. | 401 Invalid or expired JWT |
| SigningKey | SigningKey |
Primary key used to validate tokens. Used as a fallback if SigningKeys is empty. At least one of KeyFunc, JWKSetURLs, SigningKeys, or SigningKey is required. | nil |
| SigningKeys | map[string]SigningKey |
Map of keys used to validate tokens with the "kid" field. At least one of KeyFunc, JWKSetURLs, SigningKeys, or SigningKey is required. | nil |
| ContextKey | string |
Key used to store user information in the context. Optional. | "user" |
| Claims | jwt.Claims |
Defines the structure of token claims. Extendable for custom claims data. Optional. | jwt.MapClaims{} |
| TokenLookup | string |
Specifies how to extract the token from the request. Format: ":<name>" (e.g., "header:Authorization", "query:token", "param:token", "cookie:token"). Optional. |
"header:Authorization" |
| TokenProcessorFunc | func(token string) (string, error) |
Processes the token extracted using TokenLookup. Optional. |
nil |
| AuthScheme | string |
Scheme used in the Authorization header. Only used with the default TokenLookup. Optional. | "Bearer" |
| KeyFunc | func() jwt.Keyfunc |
Provides the public key for JWT verification, handling algorithm verification and key selection. At least one of KeyFunc, JWKSetURLs, SigningKeys, or SigningKey is required. | jwtKeyFunc |
| JWKSetURLs | []string |
List of URLs containing JSON Web Key Sets (JWKS) for signature verification. HTTPS is recommended. The "kid" field is mandatory in both the JWT header and the JWKS. Default behavior includes hourly refresh, auto-refresh on new "kid", rate limiting, and timeouts. | nil |
HS256 Example
package main
import (
"time"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// JWT Middleware
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
}))
// Restricted Routes
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create the Claims
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("Welcome " + name)
}HS256 Test
Login using username and password to retrieve a token.
curl --data "user=john&pass=doe" http://localhost:3000/loginResponse
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}Request a restricted resource using the token in Authorization request header.
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"Response
Welcome John DoeRS256 Example
/* Detailed source-code truncated for AI context efficiency. */RS256 Test
The RS256 is actually identical to the HS256 test above.
JWK Set Test
The tests are identical to basic JWT tests above, with exception that JWKSetURLs to valid public keys collection in JSON Web Key (JWK) Set format should be supplied. See RFC 7517.
Custom KeyFunc example
KeyFunc defines a user-defined function that supplies the public key for a token validation.
The function shall take care of verifying the signing algorithm and selecting the proper key.
A user-defined KeyFunc can be useful if tokens are issued by an external party.
When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
This is one of the three options to provide a token validation key.
The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
Required if neither SigningKeys nor SigningKey is provided.
Default to an internal implementation verifying the signing algorithm and selecting the proper key.
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
app.Use(jwtware.New(jwtware.Config{
KeyFunc: customKeyFunc(),
}))
app.Get("/ok", func(c *fiber.Ctx) error {
return c.SendString("OK")
})
}
func customKeyFunc() jwt.Keyfunc {
return func(t *jwt.Token) (interface{}, error) {
// Always check the signing method
if t.Method.Alg() != jwtware.HS256 {
return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
}
// TODO custom implementation of loading signing key like from a database
signingKey := "secret"
return []byte(signingKey), nil
}
}File: contrib_versioned_docs/version-websocket_v1.x.x/hcaptcha/README.md
id: hcaptcha
HCaptcha
A simple HCaptcha middleware to prevent bot attacks.
:::note
Requires Go 1.21 and above
:::
Install
:::caution
This middleware only supports Fiber v3.
:::
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/hcaptchaSignature
hcaptcha.New(config hcaptcha.Config) fiber.HandlerConfig
| Property | Type | Description | Default |
|---|---|---|---|
| SecretKey | string |
The secret key you obtained from the HCaptcha admin panel. This field must not be empty. | "" |
| ResponseKeyFunc | func(fiber.Ctx) (string, error) |
ResponseKeyFunc should return the token that captcha provides upon successful solving. By default, it gets the token from the body by parsing a JSON request and returns the hcaptcha_token field. |
hcaptcha.DefaultResponseKeyFunc |
| SiteVerifyURL | string |
This property specifies the API resource used for token authentication. | https://api.hcaptcha.com/siteverify |
Example
package main
import (
"github.com/gofiber/contrib/hcaptcha"
"github.com/gofiber/fiber/v3"
"log"
)
const (
TestSecretKey = "0x0000000000000000000000000000000000000000"
TestSiteKey = "20000000-ffff-ffff-ffff-000000000002"
)
func main() {
app := fiber.New()
captcha := hcaptcha.New(hcaptcha.Config{
// Must set the secret key
SecretKey: TestSecretKey,
})
app.Get("/api/", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"hcaptcha_site_key": TestSiteKey,
})
})
app.Post("/api/robots-excluded", func(c fiber.Ctx) error {
return c.SendString("You are not a robot")
}, captcha)
log.Fatal(app.Listen(":3000"))
}File: contrib_versioned_docs/version-websocket_v1.x.x/fiberzerolog/README.md
id: fiberzerolog
Fiberzerolog
Zerolog logging support for Fiber.
Note: Requires Go 1.18 and above
Install
This middleware supports Fiber v2.
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/fiberzerolog
go get -u github.com/rs/zerolog/logSignature
fiberzerolog.New(config ...fiberzerolog.Config) fiber.HandlerConfig
| Property | Type | Description | Default |
|---|---|---|---|
| Next | func(*Ctx) bool |
Define a function to skip this middleware when returned true | nil |
| Logger | *zerolog.Logger |
Add custom zerolog logger. | zerolog.New(os.Stderr).With().Timestamp().Logger() |
| GetLogger | func(*fiber.Ctx) zerolog.Logger |
Get custom zerolog logger, if it's defined the returned logger will replace the Logger value. |
nil |
| Fields | []string |
Add fields what you want see. | []string{"latency", "status", "method", "url", "error"} |
| WrapHeaders | bool | Wrap headers to dictionary.If false: {"method":"POST", "header-key":"header value"}If true: {"method":"POST", "reqHeaders": {"header-key":"header value"}} |
false |
| FieldsSnakeCase | bool | Use snake case for fields: FieldResBody, FieldQueryParams, FieldBytesReceived, FieldBytesSent, FieldRequestId, FieldReqHeaders, FieldResHeaders.If false: {"method":"POST", "resBody":"v", "queryParams":"v"}If true: {"method":"POST", "res_body":"v", "query_params":"v"} |
false |
| Messages | []string |
Custom response messages. | []string{"Server error", "Client error", "Success"} |
| Levels | []zerolog.Level |
Custom response levels. | []zerolog.Level{zerolog.ErrorLevel, zerolog.WarnLevel, zerolog.InfoLevel} |
| SkipURIs | []string |
Skip logging these URI. | []string{} |
| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | nil |
Example
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/contrib/fiberzerolog"
"github.com/rs/zerolog"
)
func main() {
app := fiber.New()
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
app.Use(fiberzerolog.New(fiberzerolog.Config{
Logger: &logger,
}))
app.Get("/", func (c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
if err := app.Listen(":3000"); err != nil {
logger.Fatal().Err(err).Msg("Fiber app error")
}
}