# Fiber > Fiber is an Express-inspired web framework written in Go. This file contains all documentation content in a single document following the llmstxt.org standard. ## 👋 Welcome [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Linter](https://github.com/gofiber/contrib/actions/workflows/lint.yml/badge.svg) Repository for third party middlewares and service implementations, with dependencies. > **Go version support:** We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## 📑 Middleware Implementations * [casbin](./casbin/README.md) * [circuitbreaker](./circuitbreaker/README.md) * [coraza](./coraza/README.md) * [fgprof](./fgprof/README.md) * [i18n](./i18n/README.md) * [sentry](./sentry/README.md) * [zap](./zap/README.md) * [zerolog](./zerolog/README.md) * [hcaptcha](./hcaptcha/README.md) * [jwt](./jwt/README.md) * [loadshed](./loadshed/README.md) * [new relic](./newrelic/README.md) * [monitor](./monitor/README.md) * [prometheus](./prometheus/README.md) * [uptime](./uptime/README.md) * [open policy agent](./opa/README.md) * [otel (opentelemetry)](./otel/README.md) * [paseto](./paseto/README.md) * [socket.io](./socketio/README.md) * [swaggo](./swaggo/README.md) _(formerly `gofiber/swagger`)_ * [swaggerui](./swaggerui/README.md) _(formerly `gofiber/contrib/swagger`)_ * [websocket](./websocket/README.md) ## 🥡 Service Implementations * [testcontainers](./testcontainers/README.md) --- ## Casbin ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*casbin*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20casbin/badge.svg) Casbin middleware for Fiber. **Compatible with Fiber v3.** > This middleware targets **Casbin v3**. Casbin v2 is no longer supported here; > if you still need it, pin the previous major > (`github.com/gofiber/contrib/v3/casbin` at its last `v1` tag). Migrating from > Casbin v2 to v3 is mostly an import-path change; see the > [Casbin upgrade guide](https://casbin.org/docs/upgrade). ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/casbin/v2 ``` choose an adapter from [here](https://casbin.org/docs/adapters) ```sh go get -u github.com/casbin/gorm-adapter/v3 ``` ## Signature ```go casbin.New(config ...casbin.Config) *casbin.Middleware ``` ## Config | Property | Type | Description | Default | |:--------------|:--------------------------|:-----------------------------------------|:--------------------------------------------------------------| | ModelFilePath | `string` | Model file path | `"./model.conf"` | | PolicyAdapter | `persist.Adapter` | Database adapter for policies | `./policy.csv` | | Enforcer | `*casbin.Enforcer` | Custom casbin enforcer | `Middleware generated enforcer using ModelFilePath & PolicyAdapter` | | Lookup | `func(fiber.Ctx) string` | Look up for current subject | `""` | | Unauthorized | `func(fiber.Ctx) error` | Response body for unauthorized responses | `Unauthorized` | | Forbidden | `func(fiber.Ctx) error` | Response body for forbidden responses | `Forbidden` | ### Examples - [Gorm Adapter](https://github.com/svcg/-fiber_casbin_demo) - [File Adapter](https://github.com/gofiber/contrib/tree/master/v3/casbin/example) ## CustomPermission ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/casbin/v2" _ "github.com/go-sql-driver/mysql" gormadapter "github.com/casbin/gorm-adapter/v3" ) func main() { app := fiber.New() adapter, _ := gormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/") authz := casbin.New(casbin.Config{ ModelFilePath: "path/to/rbac_model.conf", PolicyAdapter: adapter, Lookup: func(c fiber.Ctx) string { return "" // fetch authenticated user subject }, }) app.Post("/blog", authz.RequiresPermissions([]string{"blog:create"}, casbin.WithValidationRule(casbin.MatchAllRule)), func(c fiber.Ctx) error { // your handler }, ) app.Delete("/blog/:id", authz.RequiresPermissions([]string{"blog:create", "blog:delete"}, casbin.WithValidationRule(casbin.AtLeastOneRule)), func(c fiber.Ctx) error { // your handler }, ) app.Listen(":8080") } ``` ## RoutePermission ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/casbin/v2" _ "github.com/go-sql-driver/mysql" gormadapter "github.com/casbin/gorm-adapter/v3" ) func main() { app := fiber.New() adapter, _ := gormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/") authz := casbin.New(casbin.Config{ ModelFilePath: "path/to/rbac_model.conf", PolicyAdapter: adapter, Lookup: func(c fiber.Ctx) string { return "" // fetch authenticated user subject }, }) // check permission with Method and Path app.Post("/blog", authz.RoutePermission(), func(c fiber.Ctx) error { // your handler }, ) app.Listen(":8080") } ``` ## RoleAuthorization ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/casbin/v2" _ "github.com/go-sql-driver/mysql" gormadapter "github.com/casbin/gorm-adapter/v3" ) func main() { app := fiber.New() adapter, _ := gormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/") authz := casbin.New(casbin.Config{ ModelFilePath: "path/to/rbac_model.conf", PolicyAdapter: adapter, Lookup: func(c fiber.Ctx) string { return "" // fetch authenticated user subject }, }) app.Put("/blog/:id", authz.RequiresRoles([]string{"admin"}), func(c fiber.Ctx) error { // your handler }, ) app.Listen(":8080") } ``` --- ## Circuit Breaker ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*circuitbreaker*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20CircuitBreaker/badge.svg) A **Circuit Breaker** is a software design pattern used to prevent system failures when a service is experiencing high failures or slow responses. It helps improve system resilience by **stopping requests** to an unhealthy service and **allowing recovery** once it stabilizes. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## How It Works 1. **Closed State:** - Requests are allowed to pass normally. - Failures are counted. - If failures exceed a defined **threshold**, the circuit switches to **Open** state. 2. **Open State:** - Requests are **blocked immediately** to prevent overload. - The circuit stays open for a **timeout period** before moving to **Half-Open**. 3. **Half-Open State:** - Allows a limited number of requests to test service recovery. - If requests **succeed**, the circuit resets to **Closed**. - If requests **fail**, the circuit returns to **Open**. ## Benefits of Using a Circuit Breaker ✅ **Prevents cascading failures** in microservices. ✅ **Improves system reliability** by avoiding repeated failed requests. ✅ **Reduces load on struggling services** and allows recovery. ## Install ```bash go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/circuitbreaker ``` ## Signature ```go circuitbreaker.New(config ...circuitbreaker.Config) *circuitbreaker.Middleware ``` ## Config | Property | Type | Description | Default | |:---------|:-----|:------------|:--------| | FailureThreshold | `int` | Number of consecutive errors required to open the circuit | `5` | | Timeout | `time.Duration` | Timeout for the circuit breaker | `10 * time.Second` | | SuccessThreshold | `int` | Number of successful requests required to close the circuit | `5` | | HalfOpenMaxConcurrent | `int` | Max concurrent requests in half-open state | `1` | | Interval | `time.Duration` | Period after which failure counts reset in closed state. Zero means failures accumulate until the circuit opens. | `0` | | IsFailure | `func(error) bool` | Custom function to determine if an error is a failure | `Status >= 500` | | OnOpen | `func(fiber.Ctx) error` | Callback function when the circuit is opened | `503 response` | | OnClose | `func(fiber.Ctx) error` | Callback function when the circuit is closed | `Continue request` | | OnHalfOpen | `func(fiber.Ctx) error` | Callback function when the circuit is half-open | `429 response` | ## Circuit Breaker Usage in Fiber (Example) This guide explains how to use a Circuit Breaker in a Fiber application at different levels, from basic setup to advanced customization. ### 1. Basic Setup A **global** Circuit Breaker protects all routes. **Example: Applying Circuit Breaker to All Routes** ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/circuitbreaker" ) func main() { app := fiber.New() // Create a new Circuit Breaker with custom configuration cb := circuitbreaker.New(circuitbreaker.Config{ FailureThreshold: 3, // Max failures before opening the circuit Timeout: 5 * time.Second, // Wait time before retrying SuccessThreshold: 2, // Required successes to move back to closed state }) // Apply Circuit Breaker to ALL routes app.Use(circuitbreaker.Middleware(cb)) // Sample Route app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, world!") }) // Optional: Expose health check endpoint app.Get("/health/circuit", cb.HealthHandler()) // Optional: Expose metrics about the circuit breaker: app.Get("/metrics/circuit", func(c fiber.Ctx) error { return c.JSON(cb.GetStateStats()) }) app.Listen(":3000") // In your application shutdown logic app.Shutdown(func() { // Make sure to stop the circuit breaker when your application shuts down: cb.Stop() }) } ``` ### 2. Route & Route-Group Specific Circuit Breaker Apply the Circuit Breaker **only to specific routes**. ```go app.Get("/protected", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error { return c.SendString("Protected service running") }) ``` Apply the Circuit Breaker **only to specific routes groups**. ```go app := route.Group("/api") app.Use(circuitbreaker.Middleware(cb)) // All routes in this group will be protected app.Get("/users", getUsersHandler) app.Post("/users", createUserHandler) ``` ### 3. Circuit Breaker with Custom Failure Handling Customize the response when the circuit **opens**. ```go cb := circuitbreaker.New(circuitbreaker.Config{ FailureThreshold: 3, Timeout: 10 * time.Second, OnOpen: func(c fiber.Ctx) error { return c.Status(fiber.StatusServiceUnavailable). JSON(fiber.Map{"error": "Circuit Open: Service unavailable"}) }, OnHalfOpen: func(c fiber.Ctx) error { return c.Status(fiber.StatusTooManyRequests). JSON(fiber.Map{"error": "Circuit Half-Open: Retrying service"}) }, OnClose: func(c fiber.Ctx) error { return c.Status(fiber.StatusOK). JSON(fiber.Map{"message": "Circuit Closed: Service recovered"}) }, }) // Apply to a specific route app.Get("/custom", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error { return c.SendString("This service is protected by a Circuit Breaker") }) ``` ✅ Now, when failures exceed the threshold, ***custom error responses** will be sent. ### 4. Circuit Breaker for External API Calls Use a Circuit Breaker **when calling an external API.** ```go app.Get("/external-api", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error { // Simulating an external API call resp, err := fiber.Get("https://example.com/api") if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "External API failed") } return c.SendString(resp.Body()) }) ``` ✅ If the external API fails repeatedly, **the circuit breaker prevents further calls.** ### 5. Circuit Breaker with Concurrent Requests Handling Use a **semaphore-based** approach to **limit concurrent requests.** ```go cb := circuitbreaker.New(circuitbreaker.Config{ FailureThreshold: 3, Timeout: 5 * time.Second, SuccessThreshold: 2, HalfOpenSemaphore: make(chan struct{}, 2), // Allow only 2 concurrent requests }) app.Get("/half-open-limit", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error { time.Sleep(2 * time.Second) // Simulating slow response return c.SendString("Half-Open: Limited concurrent requests") }) ``` ✅ When in **half-open** state, only **2 concurrent requests are allowed**. ### 6. Circuit Breaker with Custom Metrics Integrate **Prometheus metrics** and **structured logging**. ```go cb := circuitbreaker.New(circuitbreaker.Config{ FailureThreshold: 5, Timeout: 10 * time.Second, OnOpen: func(c fiber.Ctx) error { log.Println("Circuit Breaker Opened!") prometheus.Inc("circuit_breaker_open_count") return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Service Down"}) }, }) ``` ✅ Logs when the circuit opens & increments Prometheus metrics. ### 7. Circuit Breaker with Failure Count Reset Interval Use `Interval` to reset the failure count once the interval has elapsed in the closed state. The reset is applied lazily: the next failure reported after the interval has elapsed starts a fresh count instead of carrying the old one over. Without `Interval`, failures accumulate indefinitely in the closed state until the threshold is reached. ```go cb := circuitbreaker.New(circuitbreaker.Config{ FailureThreshold: 5, Timeout: 10 * time.Second, Interval: 30 * time.Second, // Reset failure count every 30 seconds }) app.Use(circuitbreaker.Middleware(cb)) ``` ✅ If 4 failures occur and the next failure is reported more than 30 seconds after the window started, the count restarts at 1 instead of reaching the threshold. The circuit only opens when 5 failures accumulate within one 30-second window. ### 8. Advanced: Multiple Circuit Breakers for Different Services Use different Circuit Breakers for different services. ```go dbCB := circuitbreaker.New(circuitbreaker.Config{FailureThreshold: 5, Timeout: 10 * time.Second}) apiCB := circuitbreaker.New(circuitbreaker.Config{FailureThreshold: 3, Timeout: 5 * time.Second}) app.Get("/db-service", circuitbreaker.Middleware(dbCB), func(c fiber.Ctx) error { return c.SendString("DB service request") }) app.Get("/api-service", circuitbreaker.Middleware(apiCB), func(c fiber.Ctx) error { return c.SendString("External API service request") }) ``` ✅ Each service has its own failure threshold & timeout. --- ## Coraza ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*coraza*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Coraza/badge.svg) [Coraza](https://coraza.io/) WAF middleware for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get github.com/gofiber/fiber/v3 go get github.com/gofiber/contrib/v3/coraza ``` ## Signature ```go coraza.New(config ...coraza.Config) fiber.Handler coraza.NewEngine(config coraza.Config) (*coraza.Engine, error) ``` ## Config | Property | Type | Description | Default | |:--|:--|:--|:--| | Next | `func(fiber.Ctx) bool` | Defines a function to skip this middleware when it returns true | `nil` | | BlockHandler | `func(fiber.Ctx, coraza.InterruptionDetails) error` | Custom handler for blocked requests | `nil` | | ErrorHandler | `func(fiber.Ctx, coraza.MiddlewareError) error` | Custom handler for middleware failures | `nil` | | DirectivesFile | `[]string` | Coraza directives files loaded in order | `nil` | | RootFS | `fs.FS` | Optional filesystem used to resolve `DirectivesFile` | `nil` | | BlockMessage | `string` | Message returned by the built-in block handler | `"Request blocked by Web Application Firewall"` | | LogLevel | `fiberlog.Level` | Middleware lifecycle log level | `fiberlog.LevelInfo` in `coraza.ConfigDefault` | | RequestBodyAccess | `bool` | Enables request body inspection | `true` in `coraza.ConfigDefault` | | MetricsCollector | `coraza.MetricsCollector` | Optional custom metrics collector | `nil` (falls back to the built-in collector) | If you want the defaults, start from `coraza.ConfigDefault` and override the fields you need. For zero-value-backed settings such as `RequestBodyAccess: false`, `LogLevel: fiberlog.LevelTrace`, or resetting `MetricsCollector` to the built-in default, use `ConfigDefault` or the helper methods `WithRequestBodyAccess`, `WithLogLevel`, and `WithMetricsCollector` so the choice remains explicit. By default, the middleware starts without external rule files. Set `DirectivesFile` to load your Coraza or CRS ruleset. Request body size follows the Fiber app `BodyLimit`. Wildcard entries in `DirectivesFile` are expanded before Coraza initializes. If a wildcard matches no files, initialization fails with an error and the middleware does not start. ## Usage ```go package main import ( "log" "github.com/gofiber/contrib/v3/coraza" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() cfg := coraza.ConfigDefault cfg.DirectivesFile = []string{"./conf/coraza.conf"} app.Use(coraza.New(cfg)) app.Get("/", func(c fiber.Ctx) error { return c.SendString("ok") }) log.Fatal(app.Listen(":3000")) } ``` ## Advanced usage with Engine Use `NewEngine` when you need explicit lifecycle control, reload support, or observability data. ```go engineCfg := coraza.ConfigDefault engineCfg.DirectivesFile = []string{"./conf/coraza.conf"} engine, err := coraza.NewEngine(engineCfg) if err != nil { log.Fatal(err) } app.Use(engine.Middleware(coraza.MiddlewareConfig{ Next: func(c fiber.Ctx) bool { return c.Path() == "/healthz" }, BlockHandler: func(c fiber.Ctx, details coraza.InterruptionDetails) error { return c.Status(details.StatusCode).JSON(fiber.Map{ "blocked": true, "message": "request blocked by security policy", }) }, })) ``` For production deployments, avoid returning rule identifiers or detailed match data to clients. Prefer a generic error body and log the matched rule metadata server-side when needed. Note that the built-in block handler sets an `X-WAF-Blocked: true` response header and a message naming the Web Application Firewall. If you want to avoid WAF fingerprinting, provide a custom `BlockHandler` (or `BlockMessage`) that returns a neutral response. ## Engine observability The middleware does not open operational routes for you, but `Engine` exposes data-oriented methods that can be used to build your own endpoints: - `engine.Reload()` - `engine.MetricsSnapshot()` - `engine.Snapshot()` - `engine.Report()` `Snapshot()` and `Report()` include server filesystem paths (directive files) and raw initialization error details. Only expose such endpoints internally or behind authentication, never to untrusted clients. `Reload()` swaps in the new WAF instance immediately and then waits until requests still being inspected by the previous instance finish inspection before closing it. Inspection ends before downstream handlers run, so the wait is bounded by WAF processing time and it is safe to call `Reload()` from a handler running behind the middleware. `BlockRate` is cumulative since process start or the most recent collector reset. `RecentLatencyMs` and `RecentBlockRate` are EWMA-based recent-trend metrics with a fixed alpha of `0.2`. The EWMA is weighted per request, not per time unit: roughly the last 20 requests dominate the value, so under high traffic "recent" covers a very short time span and under low traffic a long one. The measured latency covers the full downstream handler chain, not just WAF inspection. ## Reverse proxy / trusted proxy notes When running behind Nginx, Caddy, Traefik, Cloudflare, or any other reverse proxy, make sure Fiber trusted proxy settings are configured correctly before relying on `c.IP()` or Coraza rules that depend on `REMOTE_ADDR`. If trusted proxy validation is not configured correctly, spoofed forwarding headers may cause Coraza to evaluate rules against attacker-controlled client IP values. ## Notes - Request headers and request bodies are inspected. - Request body size follows the Fiber app `BodyLimit`. - Response body inspection is not supported. - `coraza.New()` starts successfully without external rule files, but it does not load any rules until `DirectivesFile` is configured. - Invalid configuration causes `coraza.New(...)` to panic during startup, which allows applications to fail fast. ## References - [Coraza Docs](https://coraza.io/) - [OWASP Core Rule Set](https://coraza.io/docs/tutorials/coreruleset) --- ## Fgprof ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*fgprof*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Fgprof/badge.svg) [fgprof](https://github.com/felixge/fgprof) support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install Using fgprof to profiling your Fiber app. ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/fgprof ``` ## Config | Property | Type | Description | Default | |----------|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------| | Next | `func(c fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` | | Prefix | `string`. | Prefix defines a URL prefix added before "/debug/fgprof". Note that it should start with (but not end with) a slash. Example: "/federated-fiber" | `""` | ## Example ```go package main import ( "log" "github.com/gofiber/contrib/v3/fgprof" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Use(fgprof.New()) app.Get("/", func(c fiber.Ctx) error { return c.SendString("OK") }) log.Fatal(app.Listen(":3000")) } ``` ```bash go tool pprof -http=:8080 http://localhost:3000/debug/fgprof ``` --- ## HCaptcha ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*hcaptcha*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20hcaptcha/badge.svg) A simple [HCaptcha](https://hcaptcha.com) middleware to prevent bot attacks. :::note Requires Go **1.25** and above ::: **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install :::caution This middleware only supports Fiber **v3**. ::: ```shell go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/hcaptcha ``` ## Signature ```go hcaptcha.New(config hcaptcha.Config) fiber.Handler ``` ## Config | 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 the 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` | | ValidateFunc | `func(success bool, c fiber.Ctx) error` | Optional custom validation hook called after siteverify completes. Parameters: `success` (hCaptcha verification result), `c` (Fiber context). Return `nil` to continue, or return an `error` to stop request processing. If unset, middleware defaults to blocking unsuccessful verification. For secure bot protection, reject when `success == false`. | `nil` | ## Example ```go package main import ( "errors" "log" "github.com/gofiber/contrib/v3/hcaptcha" "github.com/gofiber/fiber/v3" ) 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, // Optional custom validation handling. ValidateFunc: func(success bool, c fiber.Ctx) error { if !success { if err := c.Status(fiber.StatusForbidden).JSON(fiber.Map{ "error": "HCaptcha validation failed", "details": "Please complete the captcha challenge and try again", }); err != nil { return err } return errors.New("custom validation failed") } return nil }, }) app.Get("/api/", func(c fiber.Ctx) error { return c.JSON(fiber.Map{ "hcaptcha_site_key": TestSiteKey, }) }) // Middleware order matters: place hcaptcha middleware before the final handler. app.Post("/api/submit", captcha, func(c fiber.Ctx) error { return c.SendString("You are not a robot") }) log.Fatal(app.Listen(":3000")) } ``` --- ## I18n ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*i18n*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20i18n/badge.svg) [go-i18n](https://github.com/nicksnyder/go-i18n) support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/i18n ``` ## API | Name | Signature | Description | |----------------------|--------------------------------------------------------------------------|-----------------------------------------------------------------------------| | New | `New(config ...*i18n.Config) *i18n.I18n` | Create a reusable, thread-safe localization container. | | (*I18n).Localize | `Localize(ctx fiber.Ctx, params interface{}) (string, error)` | Returns a localized message. `params` must be a message ID string or `*goi18n.LocalizeConfig`. Returns an error if the message is not found, the param type is unsupported, or `params` is nil. | | (*I18n).MustLocalize | `MustLocalize(ctx fiber.Ctx, params interface{}) string` | Like `Localize` but panics on any error. | ## Types | Name | Description | |------------------|-----------------------------------------------------------------------------------------------------| | `Loader` | Interface for loading message files. Implement `LoadMessage(path string) ([]byte, error)`. | | `LoaderFunc` | Adapter to use a plain function as a `Loader`. | | `EmbedLoader` | `Loader` implementation backed by an `embed.FS`. Use with Go's `//go:embed` directive. | ## Config | Property | Type | Description | Default | |------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------| | RootPath | `string` | The i18n template folder path. | `"./example/localize"` | | AcceptLanguages | `[]language.Tag` | A collection of languages that can be processed. | `[]language.Tag{language.Chinese, language.English}` | | FormatBundleFile | `string` | The type of the template file. | `"yaml"` | | DefaultLanguage | `language.Tag` | The default returned language type. | `language.English` | | Loader | `Loader` | The implementation of the Loader interface, which defines how to read the file. We provide both os.ReadFile and embed.FS.ReadFile. | `LoaderFunc(os.ReadFile)` | | UnmarshalFunc | `i18n.UnmarshalFunc` | The function used for decoding template files. | `yaml.Unmarshal` | | LangHandler | `func(ctx fiber.Ctx, defaultLang string) string` | Used to get the kind of language handled by fiber.Ctx and defaultLang. | Retrieved from the request header `Accept-Language` or query parameter `lang`. | ## Example ```go package main import ( "log" contribi18n "github.com/gofiber/contrib/v3/i18n" "github.com/gofiber/fiber/v3" goi18n "github.com/nicksnyder/go-i18n/v2/i18n" "golang.org/x/text/language" ) func main() { translator := contribi18n.New(&contribi18n.Config{ RootPath: "./example/localize", AcceptLanguages: []language.Tag{language.Chinese, language.English}, DefaultLanguage: language.Chinese, }) app := fiber.New() app.Get("/", func(c fiber.Ctx) error { localize, err := translator.Localize(c, "welcome") if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return c.SendString(localize) }) app.Get("/:name", func(ctx fiber.Ctx) error { return ctx.SendString(translator.MustLocalize(ctx, &goi18n.LocalizeConfig{ MessageID: "welcomeWithName", TemplateData: map[string]string{ "name": ctx.Params("name"), }, })) }) log.Fatal(app.Listen(":3000")) } ``` ## Migration from middleware usage The package now exposes a global, thread-safe container instead of middleware. To migrate existing code: 1. Remove any `app.Use(i18n.New(...))` calls—the translator no longer registers middleware. 2. Instantiate a shared translator during application startup with `translator := i18n.New(...)`. 3. Replace package-level calls such as `i18n.Localize`/`i18n.MustLocalize` with the respective methods on your translator (`translator.Localize`, `translator.MustLocalize`). 4. Drop any manual interaction with `ctx.Locals("i18n")`; all state is managed inside the translator instance. The translator instance is safe for concurrent use across handlers and reduces per-request allocations by reusing the same bundle and localizer map. --- ## JWT ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*jwt*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20jwt/badge.svg) JWT returns a JSON Web Token (JWT) auth middleware. For valid token, it sets the token in Ctx.Locals (and in the underlying `context.Context` when `PassLocalsToContext` is enabled) 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](https://echo.labstack.com/middleware/jwt) **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```bash go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/jwt go get -u github.com/golang-jwt/jwt/v5 ``` ## Signature ```go jwtware.New(config ...jwtware.Config) func(fiber.Ctx) error jwtware.FromContext(ctx any) *jwt.Token // jwt "github.com/golang-jwt/jwt/v5" ``` `FromContext` accepts a `fiber.Ctx`, `fiber.CustomCtx`, `*fasthttp.RequestCtx`, or a standard `context.Context` (e.g. the value returned by `c.Context()` when `PassLocalsToContext` is enabled). It returns a `*jwt.Token` from `github.com/golang-jwt/jwt/v5`. ## Config | Property | Type | Description | Default | |:-------------------|:-------------------------------------|:--------------------------------------------------------------------------------------|:-----------------------------| | Next | `func(fiber.Ctx) bool` | Defines a function to skip this middleware when it returns true | `nil` | | SuccessHandler | `func(fiber.Ctx) error` | Executed when a token is valid. | `c.Next()` | | ErrorHandler | `func(fiber.Ctx, error) error` | ErrorHandler defines a function which is executed for an invalid token. | `401 Invalid or expired JWT` | | SigningKey | `SigningKey` | Signing key used to validate the token. Used as a fallback if `SigningKeys` is empty. | `nil` | | SigningKeys | `map[string]SigningKey` | Map of signing keys used to validate tokens via the `kid` header. | `nil` | | Claims | `jwt.Claims` | Claims are extendable claims data defining token content. | `jwt.MapClaims{}` | | Extractor | `Extractor` | Function used to extract the token from the request. | `FromAuthHeader("Bearer")` | | TokenProcessorFunc | `func(token string) (string, error)` | TokenProcessorFunc processes the token extracted using the Extractor. | `nil` | | KeyFunc | `jwt.Keyfunc` | User-defined function that supplies the public key for token validation. | `nil` (uses internal default)| | JWKSetURLs | `[]string` | List of JSON Web Key (JWK) Set URLs used to obtain signing keys for parsing JWTs. | `nil` | | ParserOptions | `[]jwt.ParserOption` | List of [`jwt.ParserOption`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#ParserOption), provides additional options for JWT parsing. | `nil` | ## Available Extractors JWT middleware uses the shared Fiber extractors (github.com/gofiber/fiber/v3/extractors) and provides several helpers for different token sources. Import them with: ```go import "github.com/gofiber/fiber/v3/extractors" ``` For an overview and additional examples, see the Fiber Extractors guide: - https://docs.gofiber.io/guide/extractors - `extractors.FromAuthHeader(prefix string)` - Extracts token from the Authorization header using the given scheme prefix (e.g., "Bearer"). **This is the recommended and most secure method.** - `extractors.FromHeader(header string)` - Extracts token from the specified HTTP header - `extractors.FromQuery(param string)` - Extracts token from URL query parameters - `extractors.FromParam(param string)` - Extracts token from URL path parameters - `extractors.FromCookie(key string)` - Extracts token from cookies - `extractors.FromForm(param string)` - Extracts token from form data - `extractors.Chain(extrs ...extractors.Extractor)` - Tries multiple extractors in order until one succeeds ### Security Considerations ⚠️ **Security Warning**: When choosing an extractor, consider the security implications: - **URL-based extractors** (`FromQuery`, `FromParam`): Tokens can leak through server logs, browser referrer headers, proxy logs, and browser history. Use only for development or when security is not a primary concern. - **Form-based extractors** (`FromForm`): Similar risks to URL extractors, especially if forms are submitted via GET requests. - **Header-based extractors** (`FromAuthHeader`, `FromHeader`): Most secure as headers are not typically logged or exposed in referrers. - **Cookie-based extractors** (`FromCookie`): Secure for web applications but requires proper cookie security settings (HttpOnly, Secure, SameSite). **Recommendation**: Use `FromAuthHeader("Bearer")` (the default) for production applications unless you have specific requirements that necessitate alternative extractors. ## HS256 Example ```go package main import ( "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" jwtware "github.com/gofiber/contrib/v3/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")}, Extractor: extractors.FromAuthHeader("Bearer"), })) // 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 := jwtware.FromContext(c) claims := user.Claims.(jwt.MapClaims) name := claims["name"].(string) return c.SendString("Welcome " + name) } ``` ## Cookie Extractor Example ```go package main import ( "github.com/gofiber/fiber/v3" jwtware "github.com/gofiber/contrib/v3/jwt" ) func main() { app := fiber.New() // JWT Middleware with cookie extractor app.Use(jwtware.New(jwtware.Config{ SigningKey: jwtware.SigningKey{Key: []byte("secret")}, Extractor: extractors.FromCookie("token"), })) app.Get("/protected", func(c fiber.Ctx) error { return c.SendString("Protected route") }) app.Listen(":3000") } ``` ## HS256 Test _Login using username and password to retrieve a token._ ```bash curl --data "user=john&pass=doe" http://localhost:3000/login ``` _Response_ ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" } ``` _Request a restricted resource using the token in Authorization request header._ ```bash curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" ``` _Response_ ```text Welcome John Doe ``` ## RS256 Example ```go package main import ( "crypto/rand" "crypto/rsa" "log" "time" "github.com/gofiber/fiber/v3" "github.com/golang-jwt/jwt/v5" jwtware "github.com/gofiber/contrib/v3/jwt" ) var ( // Obviously, this is just a test example. Do not do this in production. // In production, you would have the private key and public key pair generated // in advance. NEVER add a private key to any GitHub repo. privateKey *rsa.PrivateKey ) func main() { app := fiber.New() // Just as a demo, generate a new private/public key pair on each run. See note above. rng := rand.Reader var err error privateKey, err = rsa.GenerateKey(rng, 2048) if err != nil { log.Fatalf("rsa.GenerateKey: %v", err) } // Login route app.Post("/login", login) // Unauthenticated route app.Get("/", accessible) // JWT Middleware app.Use(jwtware.New(jwtware.Config{ SigningKey: jwtware.SigningKey{ JWTAlg: jwtware.RS256, Key: privateKey.Public(), }, Extractor: extractors.FromAuthHeader("Bearer"), })) // 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.SigningMethodRS256, claims) // Generate encoded token and send it as response. t, err := token.SignedString(privateKey) if err != nil { log.Printf("token.SignedString: %v", err) 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 := jwtware.FromContext(c) claims := user.Claims.(jwt.MapClaims) name := claims["name"].(string) return c.SendString("Welcome " + name) } ``` ## Retrieving the token with PassLocalsToContext When `fiber.Config{PassLocalsToContext: true}` is set, the JWT token stored by the middleware is also available in the underlying `context.Context`. Use `jwtware.FromContext` with any of the supported context types: ```go // From a fiber.Ctx (most common usage) token := jwtware.FromContext(c) // From the underlying context.Context (useful in service layers or when PassLocalsToContext is enabled) token := jwtware.FromContext(c.Context()) ``` ## 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](https://www.rfc-editor.org/rfc/rfc7517). ## 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. ```go package main import ( "fmt" "github.com/gofiber/fiber/v3" jwtware "github.com/gofiber/contrib/v3/jwt" "github.com/golang-jwt/jwt/v5" ) func main() { app := fiber.New() app.Use(jwtware.New(jwtware.Config{ KeyFunc: customKeyFunc(), Extractor: extractors.FromAuthHeader("Bearer"), })) 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 } } ``` --- ## LoadShed ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*loadshed*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Loadshed/badge.svg) The LoadShed middleware for [Fiber](https://github.com/gofiber/fiber) is designed to help manage server load by shedding requests based on certain load criteria. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/loadshed ``` ## Signatures ```go loadshed.New(config ...loadshed.Config) fiber.Handler ``` ## Examples To use the LoadShed middleware in your Fiber application, import it and apply it to your Fiber app. Here's an example: ### Basic ```go package main import ( "time" "github.com/gofiber/fiber/v3" loadshed "github.com/gofiber/contrib/v3/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") } ``` ### With a custom rejection handler ```go package main import ( "time" "github.com/gofiber/fiber/v3" loadshed "github.com/gofiber/contrib/v3/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{}, }, OnShed: func(ctx fiber.Ctx) error { if ctx.Method() == fiber.MethodGet { return ctx. Status(fiber.StatusTooManyRequests). Send([]byte{}) } return ctx. Status(fiber.StatusTooManyRequests). JSON(fiber.Map{ "error": "Keep calm", }) }, })) 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{...}` | | OnShed | `func(c fiber.Ctx) error` | Function to be executed if a request should be declined | `nil` | ## 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 `LowerThreshold` and `UpperThreshold` values 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 `LowerThreshold` and `UpperThreshold`**: The probability of rejecting a request increases as the CPU usage approaches the `UpperThreshold`. This is calculated using the formula: ```plaintext rejectionProbability := (cpuUsage - LowerThreshold*100) / (UpperThreshold - LowerThreshold) ``` - **Above `UpperThreshold`**: All requests are rejected to prevent system overload. 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. ```go 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 }, OnShed: nil, } ``` --- ## Monitor ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*monitor*) ![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Monitor/badge.svg) Monitor middleware for [Fiber](https://github.com/gofiber/fiber) that reports server metrics, inspired by [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor) ![](https://i.imgur.com/nHAtBpJ.gif) **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/monitor ``` ### Signature ```go monitor.New(config ...monitor.Config) fiber.Handler ``` ### Config | 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 skip this middleware when returned true. | `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/chart.js@2.9/dist/Chart.bundle.min.js` | ### Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/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")) } ``` ### Counting all application requests The dashboard's "Total Requests" metric counts every request that passes through the monitor handler. When the middleware is mounted on a single route (as in the example above), it only counts hits on the monitor endpoint itself. To make the counter reflect the traffic of the whole application, mount it app-wide and use `Next` to limit the dashboard to a dedicated path: ```go app.Use(monitor.New(monitor.Config{ Next: func(c fiber.Ctx) bool { // Requests to all other paths are counted and passed through. return c.Path() != "/metrics" }, })) ``` ## Default Config ```go var ConfigDefault = Config{ Title: defaultTitle, Refresh: defaultRefresh, FontURL: defaultFontURL, ChartJsURL: defaultChartJSURL, CustomHead: defaultCustomHead, APIOnly: false, Next: nil, } ``` --- ## New Relic ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*newrelic*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20newrelic/badge.svg) [New Relic](https://github.com/newrelic/go-agent) support for Fiber. Only the headers the New Relic agent itself consumes are forwarded to New Relic transactions by default: the distributed tracing headers (`traceparent`, `tracestate`, `newrelic`), the synthetic monitor headers (`X-NewRelic-Synthetics`, `X-NewRelic-Synthetics-Info`) and the queue timing headers (`X-Request-Start`, `X-Queue-Start`). Any other header, including W3C `baggage`, has to be opted into with `RequestHeaderFilter`. The default is exported as `DefaultRequestHeaderFilter`, so a custom filter can build on top of it instead of restating the allowlist. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/newrelic ``` ## Signature ```go middleware.New(config middleware.Config) fiber.Handler middleware.FromContext(ctx any) *nr.Transaction // nr "github.com/newrelic/go-agent/v3/newrelic" middleware.DefaultRequestHeaderFilter(key, value string) bool ``` `FromContext` accepts a `fiber.Ctx`, `fiber.CustomCtx`, `*fasthttp.RequestCtx`, or a standard `context.Context` (e.g. the value returned by `c.Context()` when `PassLocalsToContext` is enabled). It returns an `*nr.Transaction` (a New Relic transaction from `github.com/newrelic/go-agent/v3/newrelic`). ## Config | Property | Type | Description | Default | |:-----------------------|:-----------------|:------------------------------------------------------------|:--------------------------------| | License | `string` | Required - New Relic License Key | `""` | | AppName | `string` | New Relic Application Name | `fiber-api` | | Enabled | `bool` | Enable/Disable New Relic | `false` | | ~~TransportType~~ | ~~`string`~~ | ~~Can be HTTP or HTTPS~~ (Deprecated) | ~~`"HTTP"`~~ | | Application | `Application` | Existing New Relic App | `nil` | | ErrorStatusCodeHandler | `func(c fiber.Ctx, err error) int` | If you want to change newrelic status code, you can use it. | `DefaultErrorStatusCodeHandler` | | Next | `func(c fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | RequestHeaderFilter | `func(key, value string) bool` | Return `true` to forward a request header to New Relic, `false` to skip it. | `DefaultRequestHeaderFilter` (distributed tracing, synthetics and queue timing headers only) | ## Usage ```go package main import ( "github.com/gofiber/fiber/v3" middleware "github.com/gofiber/contrib/v3/newrelic" ) func main() { app := fiber.New() app.Get("/", func(ctx fiber.Ctx) error { return ctx.SendStatus(200) }) cfg := middleware.Config{ License: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", AppName: "MyCustomApi", Enabled: true, } app.Use(middleware.New(cfg)) app.Listen(":8080") } ``` ## Usage with existing New Relic application ```go package main import ( "github.com/gofiber/fiber/v3" middleware "github.com/gofiber/contrib/v3/newrelic" nr "github.com/newrelic/go-agent/v3/newrelic" ) func main() { nrApp, err := nr.NewApplication( nr.ConfigAppName("MyCustomApi"), nr.ConfigLicense("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), nr.ConfigEnabled(true), ) app := fiber.New() app.Get("/", func(ctx fiber.Ctx) error { return ctx.SendStatus(200) }) app.Get("/foo", func(ctx fiber.Ctx) error { txn := middleware.FromContext(ctx) segment := txn.StartSegment("foo segment") defer segment.End() // do foo return nil }) cfg := middleware.Config{ Application: nrApp, } app.Use(middleware.New(cfg)) app.Listen(":8080") } ``` ## Retrieving the transaction with PassLocalsToContext When `fiber.Config{PassLocalsToContext: true}` is set, the New Relic transaction stored by the middleware is also available in the underlying `context.Context`. Use `FromContext` with any of the supported context types: ```go // From a fiber.Ctx (most common usage) txn := middleware.FromContext(c) // From the underlying context.Context (useful in service layers or when PassLocalsToContext is enabled) txn := middleware.FromContext(c.Context()) ``` --- ## OPA ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*opa*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20opa/badge.svg) [Open Policy Agent](https://github.com/open-policy-agent/opa) support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/opa ``` ## Signature ```go opa.New(config opa.Config) fiber.Handler ``` ## Config | 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 ```go 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: ```json { "method": "GET", "path": "/somePath", "query": { "name": ["John Doe"] }, "headers": { "Accept": "application/json", "Content-Type": "application/json" } } ``` ```go package main import ( "bytes" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/opa" ) func main() { app := fiber.New() module := ` package example.authz default allow := false allow if { input.method == "GET" } ` cfg := opa.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(opa.New(cfg)) app.Get("/", func(ctx fiber.Ctx) error { return ctx.SendStatus(200) }) app.Listen(":8080") } ``` --- ## OTel ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*otel*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20otel/badge.svg) [OpenTelemetry](https://opentelemetry.io/) support for Fiber. This package is listed on the [OpenTelemetry Registry](https://opentelemetry.io/registry/instrumentation-go-fiber/). **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/contrib/v3/otel ``` ## Signature ```go otel.Middleware(opts ...otel.Option) fiber.Handler ``` ## Config 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 `server.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 | | `WithTraceResponseHeader` | `string` | Specifies a response header used to expose the current trace ID. | Empty - no dedicated trace ID response header | | (❌ **Removed**) `WithServerName` | `string` | This option was removed because the `http.server_name` attribute is deprecated in the OpenTelemetry semantic conventions. The recommended attribute is `server.address`, which this middleware already fills with the hostname reported by Fiber. | - | | `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 | | `WithClientIP` | `bool` | Specifies whether to collect the client's IP address from the request. | true | | (⚠️ **Deprecated**) `WithCollectClientIP` | `bool` | Deprecated alias for `WithClientIP`. | true | | `WithoutMetrics` | `bool` | Disables metrics collection when set to true. | false | ## Usage Please refer to [example](./example) ## Metrics Notes - `http.server.request.size` and `http.server.response.size` are measured without buffering full streamed bodies into memory. - For streamed responses, size is recorded when the stream reaches EOF. - For `text/event-stream` responses (SSE), response body size is not recorded. ## Trace ID Response Header You can optionally expose the current trace ID in a dedicated response header: ```go app.Use(fiberotel.Middleware( fiberotel.WithTraceResponseHeader("X-Trace-Id"), )) ``` ## Example ```go package main import ( "context" "errors" "log" "go.opentelemetry.io/otel/sdk/resource" "github.com/gofiber/fiber/v3" fiberotel "github.com/gofiber/contrib/v3/otel" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" //"go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.39.0" oteltrace "go.opentelemetry.io/otel/trace" ) var tracer = otel.Tracer("fiber-server") func main() { tp := initTracer() defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Printf("Error shutting down tracer provider: %v", err) } }() app := fiber.New() app.Use(fiberotel.Middleware()) app.Get("/error", func(ctx fiber.Ctx) error { return errors.New("abc") }) app.Get("/users/:id", func(c fiber.Ctx) error { id := c.Params("id") name := getUser(c.Context(), id) return c.JSON(fiber.Map{"id": id, "name": name}) }) log.Fatal(app.Listen(":3000")) } func initTracer() *sdktrace.TracerProvider { exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { log.Fatal(err) } tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), sdktrace.WithResource( resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("my-service"), )), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) return tp } func getUser(ctx context.Context, id string) string { _, span := tracer.Start(ctx, "getUser", oteltrace.WithAttributes(attribute.String("id", id))) defer span.End() if id == "123" { return "otel tester" } return "unknown" } ``` --- ## Example An HTTP server using gofiber fiber and instrumentation. The server has a `/users/:id` endpoint. The server generates span information to `stdout`. These instructions expect you have [docker-compose](https://docs.docker.com/compose/) installed. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. Bring up the `fiber-server` and `fiber-client` services to run the example: ```sh docker-compose up --detach fiber-server fiber-client ``` The `fiber-client` service sends just one HTTP request to `fiber-server` and then exits. View the span generated by `fiber-server` in the logs: ```sh docker-compose logs fiber-server ``` Shut down the services when you are finished with the example: ```sh docker-compose down ``` --- ## Paseto ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*paseto*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20paseto/badge.svg) PASETO returns a Web Token (PASETO) auth middleware. - For valid token, it sets the payload data in Ctx.Locals (and in the underlying `context.Context` when `PassLocalsToContext` is enabled) and calls next handler. - For invalid token, it returns "401 - Unauthorized" error. - For missing token, it returns "400 - BadRequest" error. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/paseto go get -u github.com/o1egl/paseto ``` ## Signature ```go pasetoware.New(config ...pasetoware.Config) func(fiber.Ctx) error pasetoware.FromContext(ctx any) interface{} ``` `FromContext` accepts a `fiber.Ctx`, `fiber.CustomCtx`, `*fasthttp.RequestCtx`, or a standard `context.Context` (e.g. the value returned by `c.Context()` when `PassLocalsToContext` is enabled). ## Config | Property | Type | Description | Default | |:---------------|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------| | Next | `func(fiber.Ctx) bool` | Defines a function to skip this middleware when it returns true. | `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` | | Extractor | `Extractor` | Extractor defines a function to extract the token from the request. | `FromAuthHeader("Bearer")` | ## Available Extractors PASETO middleware uses the shared Fiber extractors (github.com/gofiber/fiber/v3/extractors) and provides several helpers for different token sources: Import them like this: ```go import "github.com/gofiber/fiber/v3/extractors" ``` For an overview and additional examples, see the Fiber Extractors guide: - https://docs.gofiber.io/guide/extractors - `extractors.FromAuthHeader(prefix string)` - Extracts token from the Authorization header using the given scheme prefix (e.g., "Bearer"). **This is the recommended and most secure method.** - `extractors.FromHeader(header string)` - Extracts token from the specified HTTP header - `extractors.FromQuery(param string)` - Extracts token from URL query parameters - `extractors.FromParam(param string)` - Extracts token from URL path parameters - `extractors.FromCookie(key string)` - Extracts token from cookies - `extractors.FromForm(param string)` - Extracts token from form data - `extractors.Chain(extrs ...extractors.Extractor)` - Tries multiple extractors in order until one succeeds ### Security Considerations ⚠️ **Security Warning**: When choosing an extractor, consider the security implications: - **URL-based extractors** (`FromQuery`, `FromParam`): Tokens can leak through server logs, browser referrer headers, proxy logs, and browser history. Use only for development or when security is not a primary concern. - **Form-based extractors** (`FromForm`): Similar risks to URL extractors, especially if forms are submitted via GET requests. - **Header-based extractors** (`FromAuthHeader`, `FromHeader`): Most secure as headers are not typically logged or exposed in referrers. - **Cookie-based extractors** (`FromCookie`): Secure for web applications but requires proper cookie security settings (HttpOnly, Secure, SameSite). **Recommendation**: Use `FromAuthHeader("Bearer")` (the default) for production applications unless you have specific requirements that necessitate alternative extractors. ## Migration from TokenPrefix If you were previously using `TokenPrefix`, you can now use `extractors.FromAuthHeader` with the prefix: ```go // Old way pasetoware.New(pasetoware.Config{ SymmetricKey: []byte("secret"), TokenPrefix: "Bearer", }) // New way pasetoware.New(pasetoware.Config{ SymmetricKey: []byte("secret"), Extractor: extractors.FromAuthHeader("Bearer"), }) ``` ## 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 ```go package main import ( "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" pasetoware "github.com/gofiber/contrib/v3/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), Extractor: extractors.FromAuthHeader("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 := pasetoware.FromContext(c).(string) return c.SendString("Welcome " + payload) } ``` #### Test it _Login using username and password to retrieve a token._ ```sh curl --data "user=john&pass=doe" http://localhost:8088/login ``` _Response_ ```json { "token": "" } ``` _Request a restricted resource using the token in Authorization request header._ ```sh curl localhost:8088/api/restricted -H "Authorization: Bearer " ``` _Response_ ```text Welcome john ``` ### SymmetricKey + Custom Validator callback ```go package main import ( "encoding/json" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" "github.com/o1egl/paseto" pasetoware "github.com/gofiber/contrib/v3/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), Extractor: extractors.FromAuthHeader("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 := pasetoware.FromContext(c).(customPayloadStruct) return c.SendString("Welcome " + payload.Name) } ``` ### Cookie Extractor Example ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" pasetoware "github.com/gofiber/contrib/v3/paseto" ) const secretSymmetricKey = "symmetric-secret-key (size = 32)" func main() { app := fiber.New() // Paseto Middleware with cookie extractor app.Use(pasetoware.New(pasetoware.Config{ SymmetricKey: []byte(secretSymmetricKey), Extractor: extractors.FromCookie("token"), })) app.Get("/protected", func(c fiber.Ctx) error { return c.SendString("Protected route") }) app.Listen(":8080") } ``` ### Query Extractor Example ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" pasetoware "github.com/gofiber/contrib/v3/paseto" ) const secretSymmetricKey = "symmetric-secret-key (size = 32)" func main() { app := fiber.New() // Paseto Middleware with query extractor app.Use(pasetoware.New(pasetoware.Config{ SymmetricKey: []byte(secretSymmetricKey), Extractor: extractors.FromQuery("token"), })) app.Get("/protected", func(c fiber.Ctx) error { return c.SendString("Protected route") }) app.Listen(":8080") } ``` ### PublicPrivate Key ```go package main import ( "crypto/ed25519" "encoding/hex" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" pasetoware "github.com/gofiber/contrib/v3/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 public (signed) token apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ Extractor: extractors.FromAuthHeader("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 sign it signedToken, err := pasetoware.CreateToken(privateKey, user, 12*time.Hour, pasetoware.PurposePublic) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } return c.JSON(fiber.Map{"token": signedToken}) } func accessible(c fiber.Ctx) error { return c.SendString("Accessible") } func restricted(c fiber.Ctx) error { payload := pasetoware.FromContext(c).(string) return c.SendString("Welcome " + payload) } ``` #### Get the payload from the context ```go payloadFromCtx := pasetoware.FromContext(c) if payloadFromCtx == nil { // Handle case where token is not in context, e.g. by returning an error return } payload := payloadFromCtx.(string) ``` `FromContext` accepts a `fiber.Ctx`, `fiber.CustomCtx`, `*fasthttp.RequestCtx`, or a standard `context.Context` (e.g. the value returned by `c.Context()` when `PassLocalsToContext` is enabled): ```go // From a fiber.Ctx (most common usage) payload := pasetoware.FromContext(c) // From the underlying context.Context (useful in service layers or when PassLocalsToContext is enabled) payload := pasetoware.FromContext(c.Context()) ``` #### Test it _Login using username and password to retrieve a token._ ```sh curl --data "user=john&pass=doe" http://localhost:8088/login ``` _Response_ ```json { "token": "" } ``` _Request a restricted resource using the token in Authorization request header._ ```sh curl localhost:8088/api/restricted -H "Authorization: Bearer " ``` _Response_ ```text Welcome John Doe ``` --- ## Prometheus ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*prometheus*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Prometheus/badge.svg) Prometheus middleware for [Fiber](https://github.com/gofiber/fiber) that instruments incoming requests and serves the metrics endpoint, based on [ansrivas/fiberprometheus](https://github.com/ansrivas/fiberprometheus). **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/prometheus ``` ## Signature ```go prometheus.New(config ...prometheus.Config) fiber.Handler ``` ## Config | Property | Type | Description | Default | |:---------|:-----|:------------|:--------| | ServiceName | `string` | Added as the `service` const label on every metric. Omitted when empty. Trimmed; must be valid UTF-8. | `""` | | Namespace | `string` | Prefixes every metric name. Trimmed; must form a valid metric name. | `"http"` | | Subsystem | `string` | Prefixes every metric name after `Namespace`. Trimmed and validated the same way. | `""` | | MetricsPath | `string` | Path served with the Prometheus exposition format. Unless `Next` returns true, requests to it are answered by the middleware and are not instrumented. Compared case-sensitively against the full request path, ignoring trailing slashes. | `"/metrics"` | | Labels | `prometheus.Labels` | Extra const labels attached to every metric. A key that is empty, starts with `__`, or collides with a reserved label (`status_code`, `status_class`, `method`, `path`, `le`) panics. | `nil` | | Registerer | `prometheus.Registerer` | Registry used to register the metrics. Reusing one across two `New` calls panics on duplicate registration. | private registry | | Gatherer | `prometheus.Gatherer` | Source the metrics endpoint gathers from. | private registry | | DisableGoCollector | `bool` | Skips registration of the Go runtime metrics collector. A registry that refuses the collector is reported to `MetricsErrorLog`, not fatal. | `false` | | DisableProcessCollector | `bool` | Skips registration of the process metrics collector. A refusal is handled as for `DisableGoCollector`. | `false` | | RequestDurationBuckets | `[]float64` | Histogram buckets for request latency, in seconds. `nil` selects the defaults; an empty non-nil slice drops the classic buckets, but only alongside `NativeHistogramBucketFactor`. Bounds must be strictly increasing, `+Inf` last only, or `New` panics. | see [Default Config](#default-config) | | RequestSizeBuckets | `[]float64` | Histogram buckets for request payload size, in bytes. | see [Default Config](#default-config) | | ResponseSizeBuckets | `[]float64` | Histogram buckets for response payload size, in bytes. | see [Default Config](#default-config) | | NativeHistogramBucketFactor | `float64` | Enables native histograms when greater than 1, capping the growth factor between buckets. Any other non-zero value panics, including a NaN or an infinity. | `0` | | NativeHistogramMaxBucketNumber | `uint32` | Bounds the native histogram buckets kept per series. | `0` (unlimited) | | NativeHistogramMinResetDuration | `time.Duration` | Minimum time before a native histogram may be reset to control its bucket count. | `0` | | TrackUnmatchedRequests | `bool` | Records metrics for requests that do not resolve to a registered route. | `false` | | UnmatchedRouteLabel | `string` | Path label used for unmatched requests when `TrackUnmatchedRequests` is enabled. Must be valid UTF-8. | `"/__unmatched__"` | | EnableOpenMetrics | `bool` | Negotiates the experimental OpenMetrics encoding. Not required for exemplars — protobuf carries them too. | `false` | | EnableOpenMetricsTextCreatedSamples | `bool` | Adds synthetic `_created` samples to OpenMetrics responses. Requires `EnableOpenMetrics`. | `false` | | DisableExemplars | `bool` | Skips trace exemplar collection, and with it the request-context read every instrumented request otherwise pays. | `false` | | DisableCompression | `bool` | Serves metrics uncompressed even when the client requests gzip or zstd. | `false` | | MetricsMaxRequestsInFlight | `int` | Caps concurrent scrapes; the excess is answered with 503. A negative panics — `promhttp` would read it as unlimited. | `0` (unlimited) | | MetricsTimeout | `time.Duration` | Bounds a single scrape before it is answered with 503. A negative panics, as for `MetricsMaxRequestsInFlight`. | `0` (no timeout) | | MetricsErrorLog | `promhttp.Logger` | Receives errors raised while gathering or writing metrics, plus the faults the middleware absorbs (a panicking `DynamicLabels` or `Next`, a refused runtime collector). A typed nil panics. | `nil` | | MetricsErrorHandling | `promhttp.HandlerErrorHandling` | How gathering errors are reported to the scraper. An unknown value panics; avoid `PanicOnError` — it takes the process down. | `promhttp.HTTPErrorOnError` | | DisabledMetrics | `[]Metric` | Metric families to skip registering and recording. Entries are trimmed; an unknown name panics. | `nil` | | SkipURIs | `[]string` | Route patterns excluded from instrumentation, e.g. `/user/:id`. A trailing `*` matches by prefix; a leading `/` is added when missing. A skipped route's error propagates normally. | `nil` | | SkipStatusCodes | `[]int` | Response status codes excluded from metrics. Codes are three digits; anything else panics. | `nil` | | SkipStatusClasses | `[]string` | Status classes excluded from metrics, `"1xx"` through `"5xx"` or `"unknown"`. Anything else panics. | `nil` | | DynamicLabels | `map[string]func(fiber.Ctx) string` | Extra labels computed per request. Names follow the same rules as `Labels`. A panicking function drops the sample. | `nil` | | Next | `func(fiber.Ctx) bool` | Skips the middleware when it returns true, including for `MetricsPath`. A panicking function is read as true. | `nil` | ## Default Config ```go var ConfigDefault = Config{ Namespace: "http", MetricsPath: "/metrics", UnmatchedRouteLabel: "/__unmatched__", RequestDurationBuckets: []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 10, 15, 30, 60}, RequestSizeBuckets: []float64{256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 5242880}, ResponseSizeBuckets: []float64{256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 5242880}, } ``` ## Example ```go package main import ( fiberprometheus "github.com/gofiber/contrib/v3/prometheus" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Mount the middleware globally so it observes every request. It answers // scrapes on Config.MetricsPath ("/metrics" by default) itself and passes // everything else through to your handlers. app.Use(fiberprometheus.New(fiberprometheus.Config{ ServiceName: "my-service-name", SkipURIs: []string{"/ping"}, SkipStatusCodes: []int{401, 403, 404}, })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello World") }) app.Get("/ping", func(c fiber.Ctx) error { return c.SendString("pong") }) app.Post("/some", func(c fiber.Ctx) error { return c.SendString("Welcome!") }) app.Listen(":3000") } ``` Register the middleware before the routes you want instrumented, and mount it only once — mounting the same handler twice double-counts every request that reaches both instances. Scrapes are the exception: the first invocation answers them without calling `ctx.Next()`, so they never reach the second. ## Metrics The following metrics are exposed, prefixed with `Namespace` (`http` by default) and `Subsystem`: ```text http_requests_total http_requests_status_class_total http_request_duration_seconds http_requests_in_progress http_request_size_bytes http_response_size_bytes ``` Every metric except `http_requests_in_progress` is labeled with the *registered route pattern* (for example `/user/:id`), not the request path, so label cardinality stays bounded no matter what clients request. Trailing slashes are trimmed from the pattern, which is what lets a `SkipURIs` entry match however it was spelled — so under `fiber.Config{StrictRouting: true}`, `/foo` and `/foo/` are two endpoints sharing one series. `http_requests_in_progress` is labeled by HTTP method only. It has to be incremented before the router picks a handler, at which point the route pattern is not known yet. `http_request_size_bytes` and `http_response_size_bytes` record a payload only when its size is known — either `Content-Length` is set, or the body is buffered and can be measured. A stream of unannounced length, such as `c.SendStream` without a size or an SSE response, is left out of those histograms rather than recorded as zero bytes, which would drag the reported percentiles towards nothing. On the request side what actually arrived is measured, so a client cannot bill the histogram for a body it never sent. The exception is a pre-parsed multipart form: fasthttp keeps the parsed parts — the large ones spilled to temp files — so reading the body back would re-marshal every uploaded file into memory just to size it, and the announced `Content-Length` is used instead — unless it exceeds `BodyLimit`, which means the body was never received in full and no honest size exists to record. Under `fiber.Config{StreamRequestBody: true}` request payloads are left out of `http_request_size_bytes` entirely. fasthttp prefetches a bounded prefix and hands the rest to the handler, and a handler that returns without reading it — an auth rejection, a 404, any route that ignores its body — leaves the remainder undrained. The announced `Content-Length` is then a client's claim rather than a measurement, and draining the stream to check would defeat the point of streaming. A response that carries no body on the wire records zero however much the handler wrote — a `HEAD`, any status RFC 9110 forbids a body on (`1xx`, `204`, `304`), or a handler that sets `c.Response().SkipBody` itself — because fasthttp drops the body and no payload bytes reach the client. `http_requests_status_class_total` is convenience, not new information: `status_class` is a function of `status_code`, so `rate(http_requests_status_class_total{status_class="5xx"}[5m])` is `rate(http_requests_total{status_code=~"5.."}[5m])`. It costs a second series per route, method and class — drop it through `DisabledMetrics` if that trade is not worth it to you. Requests that miss every registered route are not recorded unless `TrackUnmatchedRequests` is enabled, in which case they are labeled with `UnmatchedRouteLabel`. One caveat comes with that flag: a request fasthttp rejects before routing — a body over `BodyLimit`, oversized headers, a read timeout — is counted as a `200`. Fiber answers those through its server error handler, which replays the `Use` chain with non-`Use` routes skipped and writes the real status only afterwards, and Fiber v3.4.0 offers no way to tell that replay apart from an ordinary request answered by `Use` handlers. A request answered entirely by `app.Use` handlers counts as unmatched too: `static.New`, or a `Use`-mounted guard returning 401, never matches a non-`Use` route, so nothing is recorded for it by default. One case is attributed to the wrong pattern. If the matched handler delegates onwards with `c.Next()` and a trailing `app.Use` middleware runs last, Fiber has already replaced the route on the context with that middleware's mount path by the time this middleware regains control, so the request is recorded under that mount path — usually `/`. The endpoint pattern is unrecoverable at that point, and Fiber exposes no way to tell a `use` route from any other, so the case cannot be detected either. Avoid falling through from a route handler into a trailing `app.Use` if you need exact route labels. ### Dropping metrics you do not need Every family costs cardinality. `DisabledMetrics` skips registering and recording the ones you will not query — most often the two size histograms: ```go app.Use(fiberprometheus.New(fiberprometheus.Config{ DisabledMetrics: []fiberprometheus.Metric{ fiberprometheus.MetricRequestSize, fiberprometheus.MetricResponseSize, }, })) ``` ### Extra labels per request `DynamicLabels` adds labels whose values are computed once per recorded request, after the handler chain has returned. Every distinct value creates a new series, so a value taken straight from the request is a denial-of-service vector: a client that varies a header freely grows the registry without bound. Map untrusted input onto a fixed set of values before returning it: ```go var knownTenants = map[string]bool{"acme": true, "globex": true} app.Use(fiberprometheus.New(fiberprometheus.Config{ DynamicLabels: map[string]func(fiber.Ctx) string{ "tenant": func(c fiber.Ctx) string { if tenant := c.Get("X-Tenant"); knownTenants[tenant] { return tenant } return "other" }, }, })) ``` They apply to every family except `http_requests_in_progress`, which is incremented before routing and so cannot see them. Names must not collide with the reserved `status_code`, `status_class`, `method`, `path` and `le` labels or with `Labels`; the middleware panics at startup if they do. The middleware copies each returned value, so it is safe to return one of Fiber's zero-copy strings such as `c.Get(...)` or `c.Params(...)` directly. A function that panics costs its request every metric, not the request itself: the sample is dropped, the response is unaffected, and the drop is reported to `MetricsErrorLog` — once, since a bad type assertion panics on every request, and a line per request would funnel every connection through the log sink. The middleware cannot do better than drop it, because these run after the handler chain has unwound, past any `recover` the application mounted. `Config.Next` is guarded the same way, but treated as having returned true: the middleware stands aside, leaving the request — `MetricsPath` included — to the rest of the chain, rather than serving an endpoint the panicking filter may have meant to withhold. Guard your type assertions rather than relying on either. ### Filtering An entry may also name `UnmatchedRouteLabel` to exclude the traffic `TrackUnmatchedRequests` records. That is a separate namespace — this list holds route patterns, the label is a value — so a real route spelled the same as the label still obeys the pattern rules, and a rule written for real routes never takes 404 monitoring with it. The separator distinguishes the two shapes: with the label set to `/api`, `/api`, `/api*` and `/api*/` name the label, while `/api/*` is a prefix rule for the routes below it. `SkipURIs` matches the registered route pattern — note that fiberzap's option of the same name matches the request path instead. A trailing `*` matches by prefix and stops at a path segment boundary, so `/admin/*` excludes `/admin` and `/admin/users` but not `/administration`. Trailing stars are stripped as a group, so the glob spelling `/admin/**` means the same thing. `/*` excludes everything, and then no metric family is registered at all — not even the in-flight gauge, which is incremented before routing and so beyond the reach of any per-route filter. Trailing slashes are ignored, and a leading `/` is added when missing — route patterns always carry one, so `admin` without it would otherwise match nothing. The match is case-sensitive against the pattern as registered, while Fiber routes case-insensitively by default, so spell the entry the way the route was registered: `/Admin`, not `/admin`. Because Fiber route patterns can themselves end in `*`, such an entry also matches the pattern named exactly that: `/static*` excludes both the route registered as `/static*` and anything under `/static`. Blank entries are ignored in `SkipURIs` and `SkipStatusClasses` alike, so splitting an unset environment variable on `,` neither excludes the root route nor stops the process from booting; ask for `/` explicitly to skip the root. Every other unmatchable entry — an unknown metric name, a status code that is not three digits, a status class outside `"1xx"`–`"5xx"` and `"unknown"` — panics at startup rather than filtering nothing in silence. `SkipStatusCodes` takes exact codes; `SkipStatusClasses` takes whole classes so you do not have to enumerate them: ```go app.Use(fiberprometheus.New(fiberprometheus.Config{ SkipURIs: []string{"/health", "/internal/*"}, SkipStatusClasses: []string{"4xx"}, })) ``` ## Error handling Because Fiber runs the application error handler only after the whole handler chain has unwound, the middleware invokes it itself when a downstream handler returns an error. This is what Fiber's own logger middleware does, and it is what allows the recorded status code and response size to match what the client actually received. The error is therefore consumed by this middleware and does not propagate to handlers mounted *before* it. That applies only to requests it records. When the middleware stands aside — `Next` returns true, every metric family is disabled, or the route matched a `SkipURIs` pattern — there is no status code to be gained, so the error is returned unchanged and surfaces where it would without the middleware mounted. A request answered by Fiber's `timeout` middleware is recorded with the `408` the client receives. That middleware answers through fasthttp's `RequestCtx.TimeoutErrorWithCode`, which parks the response and installs it only after the whole chain has unwound, so the live response still reads `200` at the point metrics are taken; the middleware reads the parked one instead. Mount `recover.New()` **after** this middleware, not before it: ```go app.Use(prometheus.New(prometheus.Config{})) app.Use(recover.New()) ``` A panic unwinds straight past the recording step, so with `recover` mounted first the middleware never sees the request complete and the resulting 500 appears in no metric family at all — only the in-flight gauge, which is deferred, stays balanced. Recovering downstream turns the panic into an ordinary error return, which this middleware records as the 500 the client received. ## Registry and collectors By default the middleware creates a private `Registerer`/`Gatherer` pair and uses it for both registration and scraping, and registers the Go runtime and process collectors into it. When customizing the registry, ensure that `Registerer` and `Gatherer` refer to the same metrics source (for example, a `*prometheus.Registry`). Supplying only one that does not implement the other interface panics during initialization so metrics are not silently dropped. Supplying both is trusted, because that is how you pair a wrapper such as `prometheus.WrapRegistererWithPrefix` with the registry it wraps — and such a wrapper is not itself a `Gatherer`, so there is nothing to compare it against. Only a pair that is provably distinct, two different `*prometheus.Registry` values, is rejected. Pairing a wrapper with an unrelated registry is accepted and scrapes will return nothing. ```go registry := prometheus.NewRegistry() app.Use(fiberprometheus.New(fiberprometheus.Config{ ServiceName: "my-service-name", Registerer: registry, Gatherer: registry, })) ``` ### Where the endpoint is served `MetricsPath` is compared case-sensitively against the full request path, with trailing slashes ignored on both sides, which has two consequences worth knowing before you mount the middleware anywhere but the root. Mounting on a group leaves the default endpoint unreachable — `/api/metrics` never equals `/metrics`, and `/metrics` never reaches the group, so both 404. Set the full path instead: ```go api := app.Group("/api") api.Use(fiberprometheus.New(fiberprometheus.Config{MetricsPath: "/api/metrics"})) ``` And because Fiber routes case-insensitively by default while this comparison does not, `GET /METRICS` is instrumented as an ordinary request rather than answered with the exposition page. ### Exposure of the metrics endpoint The endpoint is unauthenticated. Because the middleware answers `MetricsPath` itself before routing, a route registered at the same path with auth in front of it is never reached — so restrict access at the network layer, bind the metrics listener to an internal interface, or use `Config.Next` to reject scrapes that do not come from your monitoring system. A scrape discloses more than request counts: the default collectors report the exact Go version, process memory, open file descriptors and start time, and the `path` label enumerates every registered route pattern in the application. Treat it as internal. ### Protecting the scrape endpoint A slow or stuck gather can otherwise pile up. `MetricsMaxRequestsInFlight` caps concurrent scrapes and `MetricsTimeout` bounds each one; both answer the excess with 503. Gather errors are silent by default — pass a `MetricsErrorLog` to see them: ```go app.Use(fiberprometheus.New(fiberprometheus.Config{ MetricsMaxRequestsInFlight: 4, MetricsTimeout: 10 * time.Second, MetricsErrorLog: log.New(os.Stderr, "prometheus: ", log.LstdFlags), })) ``` `MetricsTimeout` bounds the *answer*, not the gather: `promhttp` replies 503 and returns while the gathering goroutine runs on to completion, still holding its `MetricsMaxRequestsInFlight` slot. A gatherer that regularly outruns the timeout will therefore still exhaust the cap — fix the slow collector rather than raising the limit. ## Native histograms Setting `NativeHistogramBucketFactor` above 1 enables native histograms on the duration and size histograms. They resolve latency without hand-tuned buckets: the factor caps the growth between consecutive buckets, so `1.1` gives roughly 10% resolution. This requires a Prometheus server with native histograms enabled, and the values are only carried by the protobuf exposition format. Classic and native buckets are emitted together by default. To go native-only, pass an empty non-nil bucket slice — unlike `nil`, which selects the defaults. This only takes effect alongside `NativeHistogramBucketFactor`: client_golang substitutes its own defaults rather than leave a histogram with no buckets, so an empty slice on its own would give the size histograms latency-shaped buckets (`le="0.005"` … `le="10"`) measured in bytes. ```go app.Use(fiberprometheus.New(fiberprometheus.Config{ NativeHistogramBucketFactor: 1.1, NativeHistogramMaxBucketNumber: 160, NativeHistogramMinResetDuration: time.Hour, RequestDurationBuckets: []float64{}, RequestSizeBuckets: []float64{}, ResponseSizeBuckets: []float64{}, })) ``` ## Exemplars When the request context carries a **sampled** OpenTelemetry span, the duration and size histograms record the trace ID as an exemplar under the `traceID` label. Unsampled spans are skipped: Prometheus keeps one exemplar per bucket and overwrites it on each observation, so recording traces that were never exported would evict the links that lead somewhere. Reaching a scraper takes an encoding that carries exemplars. Protobuf does, and promhttp negotiates it without any configuration — which is what a Prometheus server asks for once native histograms are enabled. OpenMetrics text does too, but only when you set `EnableOpenMetrics: true`; the plain text exposition carries no exemplars in either case. Collecting one costs a request-context read on every instrumented request: Fiber installs a background context when the application never set one, which the request then has to clear again on release. Set `DisableExemplars: true` when nothing in your stack starts spans, and that work goes away. ## 📊 Result - Hit the default url at [http://localhost:3000](http://localhost:3000) - Navigate to [http://localhost:3000/metrics](http://localhost:3000/metrics) --- ## Sentry ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*sentry*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20sentry/badge.svg) [Sentry](https://sentry.io/) support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/sentry go get -u github.com/getsentry/sentry-go ``` ## Signature ```go fiberSentry.New(config ...fiberSentry.Config) fiber.Handler fiberSentry.GetHubFromContext(ctx any) *sdk.Hub // sdk "github.com/getsentry/sentry-go" fiberSentry.MustGetHubFromContext(ctx any) *sdk.Hub // sdk "github.com/getsentry/sentry-go" ``` `GetHubFromContext` and `MustGetHubFromContext` each accept a `fiber.Ctx`, `fiber.CustomCtx`, `*fasthttp.RequestCtx`, or a standard `context.Context` (e.g. the value returned by `c.Context()` when `PassLocalsToContext` is enabled). The `Must*` variant panics if the hub is not found. `*sdk.Hub` is `*sentry.Hub` from `github.com/getsentry/sentry-go`. ## Config | Property | Type | Description | Default | | :-------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------- | | Repanic | `bool` | Repanic configures whether Sentry should repanic after recovery. Set to true, if [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used. | `false` | | WaitForDelivery | `bool` | WaitForDelivery configures whether you want to block the request before moving forward with the response. If [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used, it's safe to either skip this option or set it to false. | `false` | | Timeout | `time.Duration` | Timeout for the event delivery requests. | `time.Second * 2` | ## Usage `sentry` attaches an instance of `*sentry.Hub` (https://godoc.org/github.com/getsentry/sentry-go#Hub) to the request's context, which makes it available throughout the rest of the request's lifetime. You can access it by using the `sentry.GetHubFromContext()` or `sentry.MustGetHubFromContext()` method on the context itself in any of your proceeding middleware and routes. Keep in mind that `*sentry.Hub` should be used instead of the global `sentry.CaptureMessage`, `sentry.CaptureException`, or any other calls, as it keeps the separation of data between the requests. - **Keep in mind that `*sentry.Hub` won't be available in middleware attached before `sentry`. In this case, `GetHubFromContext()` returns nil, and `MustGetHubFromContext()` will panic.** ```go package main import ( "fmt" "log" sdk "github.com/getsentry/sentry-go" fiberSentry "github.com/gofiber/contrib/v3/sentry" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/utils" ) func main() { _ = sdk.Init(sdk.ClientOptions{ Dsn: "", BeforeSend: func(event *sdk.Event, hint *sdk.EventHint) *sdk.Event { if hint.Context != nil { if c, ok := hint.Context.Value(sdk.RequestContextKey).(fiber.Ctx); ok { // You have access to the original Context if it panicked fmt.Println(utils.ImmutableString(c.Hostname())) } } fmt.Println(event) return event }, Debug: true, AttachStacktrace: true, }) app := fiber.New() app.Use(fiberSentry.New(fiberSentry.Config{ Repanic: true, WaitForDelivery: true, })) enhanceSentryEvent := func(c fiber.Ctx) error { if hub := fiberSentry.GetHubFromContext(c); hub != nil { hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt") } return c.Next() } app.All("/foo", enhanceSentryEvent, func(c fiber.Ctx) error { panic("y tho") }) app.All("/", func(c fiber.Ctx) error { if hub := fiberSentry.GetHubFromContext(c); hub != nil { hub.WithScope(func(scope *sdk.Scope) { scope.SetExtra("unwantedQuery", "someQueryDataMaybe") hub.CaptureMessage("User provided unwanted query string, but we recovered just fine") }) } return c.SendStatus(fiber.StatusOK) }) log.Fatal(app.Listen(":3000")) } ``` ## Accessing Context in `BeforeSend` callback ```go import ( "fmt" "github.com/gofiber/fiber/v3" sdk "github.com/getsentry/sentry-go" ) sdk.Init(sdk.ClientOptions{ Dsn: "your-public-dsn", BeforeSend: func(event *sdk.Event, hint *sdk.EventHint) *sdk.Event { if hint.Context != nil { if c, ok := hint.Context.Value(sdk.RequestContextKey).(fiber.Ctx); ok { // You have access to the original Context if it panicked fmt.Println(c.Hostname()) } } return event }, }) ``` ## Retrieving the hub with PassLocalsToContext When `fiber.Config{PassLocalsToContext: true}` is set, the Sentry hub stored by the middleware is also available in the underlying `context.Context`. Use `GetHubFromContext` or `MustGetHubFromContext` with any of the supported context types: ```go // From a fiber.Ctx (most common usage) hub := fiberSentry.GetHubFromContext(c) // From the underlying context.Context (useful in service layers or when PassLocalsToContext is enabled) hub := fiberSentry.GetHubFromContext(c.Context()) ``` `MustGetHubFromContext` panics if the hub is not found (e.g. in middleware that runs before `sentry`): ```go hub := fiberSentry.MustGetHubFromContext(c) ``` --- ## Socket.io ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*socketio*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Socket.io/badge.svg) WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) that implements the [Engine.IO v4](https://github.com/socketio/engine.io-protocol) / [Socket.IO v5](https://github.com/socketio/socket.io-protocol) wire protocol, making it fully compatible with the official [`socket.io-client`](https://socket.io/docs/v4/client-api/) library. For applications that used older `socketio` releases as a plain WebSocket event bus, migrate to `github.com/gofiber/contrib/v3/websocket/event`. A deprecated compatibility shim is available at `github.com/gofiber/contrib/v3/socketio/legacy`. **Compatible with Fiber v3.** ## Features This middleware implements the full Engine.IO v4 / Socket.IO v5 wire protocol. Highlights: - **Synchronous handshake.** The Engine.IO OPEN / Socket.IO CONNECT exchange completes before the user `New()` callback returns, so emits issued inside the callback are ordered after the handshake reply. - **HTTP long-polling fallback (opt-in).** Set `socketio.EnablePolling = true` and mount the same handler for `GET` and `POST` to accept `transport=polling` clients. Polling sessions speak the same Engine.IO v4 / Socket.IO v5 wire protocol over HTTP and route through the same listener API (Emit, Ack, Close, Broadcast). Polling-to-WebSocket transport upgrade is not yet implemented; sessions that connect via polling stay on polling. - **Namespaces and handshake auth.** The negotiated namespace is honoured for inbound and outbound packets; the client's connect-time `auth` payload is exposed via `Websocket.HandshakeAuth()` and `EventPayload.HandshakeAuth`. - **Inbound acks.** Client-initiated callbacks surface as `EventPayload.HasAck` / `AckID`; reply once with `payload.Ack(args...)`. - **Outbound acks.** Server-initiated `EmitWithAck`, `EmitWithAckTimeout`, and `EmitWithAckArgs` round-trip a callback id and invoke the supplied callback when the client acks (or on timeout/disconnect). - **Multi-arg events.** Inbound events expose every argument tuple as `EventPayload.Args [][]byte`; outbound `EmitArgs` / `EmitWithAckArgs` send pre-encoded JSON tuples. - **Deterministic heartbeat.** Server PINGs every `PingInterval`; the connection is torn down if no PONG arrives within `PingTimeout`. - **EIO 0x1E batched frames.** Multi-packet WebSocket frames separated by ASCII RS (`0x1E`) are parsed correctly, with a hard cap (`MaxBatchPackets`) to prevent slice-header amplification. - **Reserved-event-name guard.** User code cannot register or emit names reserved by the protocol (e.g. `connect`, `disconnect`). - **EIO version validation.** Handshakes that advertise an unsupported `EIO` version are rejected. - **Auth payload validation.** The auth blob must be a JSON object and is bounded by `MaxAuthPayload`; oversize or malformed payloads are answered with CONNECT_ERROR. - **DoS hardening.** `MaxPayload`, `MaxBatchPackets`, `MaxEventNameLength`, and `MaxAuthPayload` bound every attacker-controlled length. - **Lock-free listener registry** plus `atomic.Bool isAlive`, removing the per-event mutex from the hot path. - **Optional drop-frames-on-overflow.** When `DropFramesOnOverflow` is true, a saturated send queue drops the offending frame and fires `EventError` instead of tearing down the connection. - **Graceful drain.** The package-level `Shutdown(ctx)` closes every active socket and waits for each worker to exit (or until `ctx` is cancelled). ## Known limitations - **One namespace per Engine.IO connection.** Each WebSocket binds the namespace negotiated during the SIO CONNECT packet; multiplexing several namespaces over one EIO connection is not supported. - **No BINARY_EVENT (5) / BINARY_ACK (6).** Binary Socket.IO frames are passed through as raw `EventMessage` data; attachment reassembly is not implemented. - **No connection-state recovery.** Resume-on-reconnect (Socket.IO's `connectionStateRecovery` feature) is not implemented; reconnects always start a fresh session. - **No polling-to-WebSocket transport upgrade.** When polling is enabled, sessions that open with `transport=polling` advertise an empty `upgrades` array and stay on polling for the session lifetime. Clients that need WebSocket from the start should configure `transports: ['websocket']`. - **No JSONP polling fallback.** JSONP requests (`?j=N`) are rejected with engine.io error code 3. Modern browsers use XHR2/fetch; JSONP support is not planned. - **CORS is not handled by the middleware.** Mount `github.com/gofiber/fiber/v3/middleware/cors` (or your preferred CORS middleware) upstream of the polling route to control the policy. Long-poll holds connections open for up to ~25s by default, so reverse-proxy timeouts must accommodate (e.g. nginx `proxy_read_timeout >= 60s` and `proxy_buffering off`). #### Production hardening notes - **Rate limiting**. Each polling open allocates a `*Websocket` plus 2 short-lived goroutines. With `EnablePolling = true` an unauthenticated client can create sessions until `HandshakeTimeout` reaps idle ones (10s default). Mount `github.com/gofiber/fiber/v3/middleware/limiter` upstream of the route to bound concurrent session creation. - **Write timeout**. A long-poll GET response that the client never reads pins a fasthttp worker on TCP backpressure. Configure `fiber.Config{WriteTimeout: ...}` (a few seconds is typically appropriate) so abandoned reads do not strand workers. - **Burst sizing**. `PollQueueMaxFrames` (default `1024`) bounds the per-session outbound buffer. With the default `DropFramesOnOverflow = false` a synchronous burst of more than 1024 emits inside a single listener call disconnects the session with `ErrSendQueueClosed`. Either pace large bursts across drains, raise `PollQueueMaxFrames`, or set `DropFramesOnOverflow = true` to tolerate overflow at the cost of dropped frames + `EventError`. - **Listener panics**. Both transports recover panics inside the `New()` callback and inside event listeners; the panic value is logged via the package `Logger` hook. Avoid `panic(string(attackerControlledBytes))` to prevent log injection in downstream consumers. ## Configuration All tunables are package-level variables; override before the first connection is accepted. | Variable | Default | Meaning | |:-----------------------|:-------------------|:------------------------------------------------------------------------------| | `PingInterval` | `25s` | How often the server emits Engine.IO PING. | | `PingTimeout` | `20s` | Grace window for the client PONG before the connection is killed. | | `HandshakeTimeout` | `10s` | Hard deadline for completing EIO OPEN + SIO CONNECT. | | `MaxPayload` | `1_000_000` (1 MB) | Max bytes per inbound WebSocket frame; advertised to the client. | | `MaxAuthPayload` | `8 KiB` | Max bytes for the SIO CONNECT auth JSON. | | `MaxBatchPackets` | `256` | Max EIO packets in a single `0x1E`-batched frame. | | `MaxEventNameLength` | `256` | Max length of an inbound SIO event name. | | `OutboundAckTimeout` | `30s` | Default ack deadline for `EmitWithAck`. | | `SendQueueSize` | `100` | Capacity of the per-connection outbound queue. | | `DropFramesOnOverflow` | `false` | If true, drop the offending frame on overflow (fires `EventError`). | | `RetrySendTimeout` | `20ms` | Back-off between send retries. | | `MaxSendRetry` | `5` | Max send retries before a frame is dropped. | | `ReadTimeout` | `10ms` | Deprecated: no longer consulted by the read loop; kept for backward compatibility. | | `EnablePolling` | `false` | If true, the handler returned from `New` also serves Engine.IO HTTP long-polling on `GET`/`POST`. | | `PollingMaxBufferSize` | `1_000_000` | Cap on a single polling HTTP body (request POST or response GET drain). | | `MaxPollWait` | `30s` | Maximum time a long-poll GET blocks waiting for outbound frames. | | `PollQueueMaxFrames` | `1024` | Cap on buffered outbound frames per polling session; overflow honors `DropFramesOnOverflow`. | Use `socketio.Shutdown(ctx)` from `fiber.App.ShutdownWithContext` for a deterministic drain. ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/socketio ``` ## Protocol compatibility The middleware automatically handles the Engine.IO / Socket.IO handshake so you do **not** need any special server-side code; just point your `socket.io-client` at the WebSocket endpoint. ### Required client configuration The default `socket.io-client` transport order is `['polling', 'websocket']`. The middleware supports both, but polling is **opt-in**: **WebSocket only (default):** ```js import { io } from "socket.io-client"; const socket = io("http://localhost:3000", { path: "/ws", // match the Fiber route transports: ["websocket"], // skip polling }); ``` **Polling (or polling + websocket fallback):** enable `EnablePolling` server-side and mount the handler for both `GET` and `POST`: ```go socketio.EnablePolling = true h := socketio.New(func(kws *socketio.Websocket) { /* ... */ }) app.Get("/ws", h) app.Post("/ws", h) // Optionally allow CORS preflight: // app.Options("/ws", h) ``` ```js import { io } from "socket.io-client"; // Default transport order: polling first, then upgrade attempt. Since this // implementation does not yet upgrade polling sessions to WebSocket, the // session stays on polling. For a forced WebSocket connect use // transports: ["websocket"]; for polling-only use transports: ["polling"]. const socket = io("http://localhost:3000", { path: "/ws", }); ``` > CORS is not handled by the middleware. If your client connects from a different origin, mount your preferred CORS middleware (e.g. `github.com/gofiber/fiber/v3/middleware/cors`) upstream of the route. Long-polling holds a request open for up to ~25s by default, so reverse-proxy timeouts must accommodate (`proxy_read_timeout` >= 60s on nginx, `proxy_buffering off`). #### Polling pitfalls - **Forgot to mount POST.** Polling clients send packets via POST; without `app.Post(path, h)` (or `app.All(...)`) the server returns 404 and the client loops with `transport error`. Always mount both `GET` and `POST` for polling routes. - **`kws.Conn` is nil on polling.** Use `kws.IsPolling()` to branch, or stick to the transport-agnostic `Emit`, `EmitEvent`, `EmitArgs`, `EmitWithAck*`, `Broadcast`, `Ack`, and `Close` methods. They all work identically on both transports. - **Snapshot vs live request state.** `kws.Locals`, `kws.Params`, `kws.Query`, `kws.Cookies` are captured at session-open time on polling sessions (because fasthttp recycles the request context after the OPEN handler returns). Store mutable per-connection data via `kws.SetAttribute` instead. - **Burst bigger than `PollQueueMaxFrames`.** With the default `DropFramesOnOverflow = false`, emitting more than `PollQueueMaxFrames` (1024) frames before any GET drains them tears the session down with `ErrSendQueueClosed`. Either pace bursts, raise `PollQueueMaxFrames`, or set `DropFramesOnOverflow = true` to drop the offending frames + fire `EventError(ErrSendQueueOverflow)` instead. - **Body limit collision.** If your Fiber app sets `BodyLimit` lower than `PollingMaxBufferSize`, fasthttp rejects the POST before our handler runs. Keep `BodyLimit` >= `PollingMaxBufferSize`. ### Tunable globals These package-level variables can be overridden before the first connection is accepted (typically in `init()` or early in `main`). They control timing and limits for the Engine.IO / Socket.IO transport. | Variable | Default | Description | |:--------------------|:-------------------|:-----------------------------------------------------------------------------------------------------| | `PingInterval` | `25 * time.Second` | Interval between Engine.IO PING frames sent by the server to keep the connection alive. | | `PingTimeout` | `20 * time.Second` | How long the server waits for the client's PONG before considering the connection dead. | | `HandshakeTimeout` | `10 * time.Second` | Maximum time allowed for the Engine.IO / Socket.IO handshake (including namespace CONNECT) to complete. | | `MaxPayload` | `1 << 20` (1 MiB) | Maximum size in bytes for a single inbound WebSocket frame; oversize messages close the socket. | | `MaxAuthPayload` | `8 << 10` (8 KiB) | Maximum size in bytes for the Socket.IO CONNECT auth JSON. | | `MaxBatchPackets` | `256` | Maximum number of Engine.IO packets accepted in a single `0x1E`-batched frame. | | `MaxEventNameLength`| `256` | Maximum length of an inbound Socket.IO event name. | | `OutboundAckTimeout`| `30 * time.Second` | Default timeout used by `EmitWithAck` when no per-call timeout is supplied. | | `DropFramesOnOverflow` | `false` | If true, saturated outbound queues drop the offending frame and fire `EventError`. | | `RetrySendTimeout` | `20 * time.Millisecond` | Back-off between WebSocket send retries. | | `MaxSendRetry` | `5` | Maximum number of WebSocket send retries before a frame is dropped. | | `EnablePolling` | `false` | If true, the handler also accepts Engine.IO HTTP long-polling on `GET`/`POST` (opt-in fallback). | | `PollingMaxBufferSize` | `1_000_000` | Cap on a single polling HTTP body (POST request body or GET drain response body), in bytes. | | `MaxPollWait` | `30 * time.Second` | Maximum time a long-poll GET blocks waiting for outbound frames before returning an empty 200. | | `PollQueueMaxFrames`| `1024` | Maximum buffered outbound frames per polling session before overflow handling applies. | ```go func init() { socketio.PingInterval = 15 * time.Second socketio.PingTimeout = 10 * time.Second socketio.MaxPayload = 4 << 20 // 4 MiB } ``` ### Message format All messages are exchanged as Socket.IO events. | Side | API call | Wire format | |:----------------|:--------------------------------------|:-----------------------------------| | Server → Client | `kws.Emit([]byte("hello"))` | `42["message","hello"]` | | Server → Client | `kws.EmitEvent("greet", data)` | `42["greet",]` | | Client → Server | `socket.emit("message", obj)` | fires `EventMessage` with `obj` | | Client → Server | `socket.emit("custom", obj)` | fires the `"custom"` event | > **Note:** `Emit`, `EmitEvent`, `EmitArgs`, and ack-emitting variants pass valid JSON through unchanged. Raw text bytes are encoded as JSON strings for compatibility with older examples. ### Acks, namespaces, handshake auth The middleware implements the full Socket.IO v5 ack flow and forwards the client's connect-time auth payload to your handlers. #### Multi-argument emits `EmitArgs` and `EmitWithAckArgs` accept a variadic list of values, so you can send richer event tuples without manually concatenating arrays. Valid JSON is passed through unchanged; raw text is encoded as a JSON string. ```go // 42["greet","hi",{"id":1}] kws.EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`)) ``` #### Server-initiated acks `EmitWithAck` (and `EmitWithAckTimeout`) emit an event with an ack id and invoke the supplied callback once the client acks, or with an error when the timeout expires. `EmitWithAck` uses `OutboundAckTimeout`; `EmitWithAckTimeout` takes a per-call duration plus a structured `AckCallback` that distinguishes timeout from disconnect. ```go kws.EmitWithAckTimeout("ping", []byte(`"hello"`), 3*time.Second, func(ack []byte, err error) { if err != nil { log.Printf("ack failed: %v", err) return } // ack is the raw JSON the client passed to its callback (single value // or a JSON-array literal for multi-arg acks). }) ``` #### Client-initiated acks When the client emits with a callback, the inbound event payload carries an ack id. Use `HasAck` and `AckID` to detect it, then send a single ack reply via `EventPayload.Ack`: ```go socketio.On("greet", func(ep *socketio.EventPayload) { if ep.HasAck { // ep.Args holds the raw JSON arguments the client sent. _ = ep.Ack([]byte(`"ok"`)) } }) ``` #### Namespaces The middleware honours the namespace negotiated during the Socket.IO CONNECT packet. Events emitted from the server are routed back on the same namespace the client joined; no extra configuration is required on the Go side. #### Handshake auth The client's `auth` payload must be a JSON object. It is parsed during the Socket.IO handshake and exposed to handlers as `EventPayload.HandshakeAuth` (raw JSON bytes). It is most commonly inspected on `EventConnect`: ```js // client const socket = io("http://localhost:3000", { path: "/ws", transports: ["websocket"], auth: { token: "secret" }, }); ``` ```go socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) { // ep.HandshakeAuth == []byte(`{"token":"secret"}`) var auth struct{ Token string `json:"token"` } _ = json.Unmarshal(ep.HandshakeAuth, &auth) }) ``` ## Signatures ```go // Initialize new socketio in the callback this will // execute a callback that expects kws *Websocket Object // and optional config websocket.Config func New(callback func(kws *Websocket), config ...websocket.Config) func(fiber.Ctx) error ``` ```go // Add listener callback for an event into the listeners list func On(event string, callback func(payload *EventPayload)) ``` ```go // Emit the message to a specific socket uuids list // Ignores all errors func EmitToList(uuids []string, message []byte, mType ...int) ``` ```go // Emit to a specific socket connection func EmitTo(uuid string, message []byte, mType ...int) error ``` ```go // Broadcast to all the active connections func Broadcast(message []byte, mType ...int) ``` ```go // Fire custom event on all connections func Fire(event string, data []byte) ``` ```go // Emit a named event with multiple arguments // (e.g. EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`))) func (kws *Websocket) EmitArgs(event string, args ...[]byte) ``` ```go // Emit a named event and invoke cb when the client acks (or on timeout / // disconnect). The default deadline is OutboundAckTimeout. The callback // receives the raw JSON ack value (or nil on timeout/disconnect). func (kws *Websocket) EmitWithAck(event string, data []byte, cb func(ack []byte)) ``` ```go // Like EmitWithAck but with a per-call timeout and a structured AckCallback // that distinguishes ErrAckTimeout from ErrAckDisconnected. Pass timeout = 0 // to disable the timeout. func (kws *Websocket) EmitWithAckTimeout(event string, data []byte, timeout time.Duration, cb AckCallback) ``` ```go // Multi-argument variant of EmitWithAck. The callback receives the slice of // raw ack arguments the client supplied (or an error on timeout / // disconnect). Uses OutboundAckTimeout. func (kws *Websocket) EmitWithAckArgs(event string, args [][]byte, cb func([][]byte, error)) ``` ```go // HandshakeAuth returns the raw JSON auth payload sent by the client at // connect time (nil if the client did not provide one). func (kws *Websocket) HandshakeAuth() json.RawMessage ``` ```go // Ack sends a Socket.IO ACK frame back to the client for the inbound event // represented by this payload. Idempotent: only the first invocation // produces a wire frame; later calls return ErrAckAlreadySent. func (ep *EventPayload) Ack(args ...[]byte) error ``` ## Example ### Go server ```go package main import ( "encoding/json" "fmt" "log" "github.com/gofiber/contrib/v3/socketio" "github.com/gofiber/contrib/v3/websocket" "github.com/gofiber/fiber/v3" ) // MessageObject Basic chat message object type MessageObject struct { Data string `json:"data"` From string `json:"from"` Event string `json:"event"` To string `json:"to"` } func main() { // The key for the map is message.to clients := make(map[string]string) // Start a new Fiber application app := fiber.New() // Setup the middleware to retrieve the data sent in first GET request app.Use(func(c fiber.Ctx) error { // IsWebSocketUpgrade returns true if the client // requested upgrade to the WebSocket protocol. if websocket.IsWebSocketUpgrade(c) { c.Locals("allowed", true) return c.Next() } return fiber.ErrUpgradeRequired }) // Multiple event handling supported socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) { fmt.Printf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id")) }) // Custom event handling supported socketio.On("CUSTOM_EVENT", func(ep *socketio.EventPayload) { fmt.Printf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id")) // ---> // DO YOUR BUSINESS HERE // ---> }) // On message event socketio.On(socketio.EventMessage, func(ep *socketio.EventPayload) { fmt.Printf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data)) message := MessageObject{} // Unmarshal the json message // { // "from": "", // "to": "", // "event": "CUSTOM_EVENT", // "data": "hello" //} err := json.Unmarshal(ep.Data, &message) if err != nil { fmt.Println(err) return } // Fire custom event based on some // business logic if message.Event != "" { ep.Kws.Fire(message.Event, []byte(message.Data)) } // Emit the message directly to specified user err = ep.Kws.EmitTo(clients[message.To], ep.Data, socketio.TextMessage) if err != nil { fmt.Println(err) } }) // On disconnect event socketio.On(socketio.EventDisconnect, func(ep *socketio.EventPayload) { // Remove the user from the local clients delete(clients, ep.Kws.GetStringAttribute("user_id")) fmt.Printf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id")) }) // On close event // This event is called when the server disconnects the user actively with .Close() method socketio.On(socketio.EventClose, func(ep *socketio.EventPayload) { // Remove the user from the local clients delete(clients, ep.Kws.GetStringAttribute("user_id")) fmt.Printf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id")) }) // On error event socketio.On(socketio.EventError, func(ep *socketio.EventPayload) { fmt.Printf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id")) }) app.Get("/ws/:id", socketio.New(func(kws *socketio.Websocket) { // Retrieve the user id from endpoint userId := kws.Params("id") // Add the connection to the list of the connected clients // The UUID is generated randomly and is the key that allow // socketio to manage Emit/EmitTo/Broadcast clients[userId] = kws.UUID // Every websocket connection has an optional session key => value storage kws.SetAttribute("user_id", userId) // Broadcast to all the connected users the newcomer newUserMsg, _ := json.Marshal(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)) kws.Broadcast(newUserMsg, true, socketio.TextMessage) // Write welcome message. Raw text is encoded as a JSON string. welcomeMsg, _ := json.Marshal(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)) kws.Emit(welcomeMsg, socketio.TextMessage) })) log.Fatal(app.Listen(":3000")) } ``` ### TypeScript / JavaScript client ```ts import { io } from "socket.io-client"; const socket = io("http://localhost:3000", { path: "/ws", transports: ["websocket"], }); socket.on("connect", () => { console.log("connected, sid =", socket.id); // Send a message to the server socket.emit("message", { from: "user1", to: "user2", event: "", data: "hello", }); }); socket.on("message", (data: unknown) => { console.log("received message:", data); }); socket.on("disconnect", (reason) => { console.log("disconnected:", reason); }); ``` --- ## Supported events | Const | Event | Description | |:----------------|:-------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| | EventMessage | `message` | Fired when a `socket.emit("message", …)` event is received from the client | | EventPing | `ping` | Fired when a WebSocket PING control frame is received (RFC 6455). Engine.IO PING is server-originated and not surfaced via this event. | | EventPong | `pong` | Fired when an Engine.IO PONG (`"3"`) replies to the server's heartbeat or when a WebSocket PONG control frame is received. | | EventDisconnect | `disconnect` | Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7. | | EventConnect | `connect` | Fired after the Engine.IO / Socket.IO handshake completes; `ep.HandshakeAuth` is populated with the client's `auth` payload (raw JSON, nil if not provided) | | EventClose | `close` | Fired when the connection is actively closed from the server. Different from client disconnection | | EventError | `error` | Fired when some error appears useful also for debugging websockets | Custom events map directly to the event name used in `socket.emit("myEvent", …)` on the client and `kws.EmitEvent("myEvent", data)` on the server. ## Event Payload object | Variable | Type | Description | |:-----------------|:--------------------|:--------------------------------------------------------------------------------------------------| | Kws | `*Websocket` | The connection object | | Name | `string` | The name of the event | | SocketUUID | `string` | Unique connection UUID | | SocketAttributes | `map[string]any` | Optional websocket attributes | | Error | `error` | (optional) Fired from disconnection or error events | | Data | `[]byte` | Raw JSON of the event payload (first argument of `socket.emit`) | | Args | `[][]byte` | All raw JSON arguments after the event name; useful when the client emits multiple values | | AckID | `uint64` | Ack id assigned by the client when it emitted with a callback (0 if `HasAck` is false) | | HasAck | `bool` | True when the inbound event expects an ack reply; respond via `EventPayload.Ack(args...)` | | HandshakeAuth | `json.RawMessage` | Raw JSON auth payload from the Socket.IO handshake; populated on `EventConnect` listeners (use `Kws.HandshakeAuth()` elsewhere) | ## Socket instance functions | Name | Type | Description | |:--------------------|:-------------------|:---------------------------------------------------------------------------------------------| | SetAttribute | `void` | Set a specific attribute for the specific socket connection | | GetUUID | `string` | Get socket connection UUID | | SetUUID | `error` | Set socket connection UUID | | GetAttribute | `string` | Get a specific attribute from the socket attributes | | EmitToList | `void` | Emit the message to a specific socket uuids list | | EmitTo | `error` | Emit to a specific socket connection | | Broadcast | `void` | Broadcast to all the active connections except broadcasting the message to itself | | Fire | `void` | Fire custom event | | Emit | `void` | Send data as a `"message"` socket.io event; valid JSON is passed through, raw text is JSON-encoded | | EmitEvent | `void` | Send a named socket.io event; valid JSON is passed through, raw text is JSON-encoded | | EmitArgs | `void` | Emit a named event with multiple arguments; valid JSON is passed through, raw text is JSON-encoded | | EmitWithAck | `void` | Emit an event and invoke `cb(ack)` when the client acks (uses `OutboundAckTimeout`) | | EmitWithAckTimeout | `void` | Like `EmitWithAck` but with a per-call timeout and a structured `AckCallback` | | EmitWithAckArgs | `void` | Multi-arg variant; `cb([][]byte, error)` receives the ack tuple (uses `OutboundAckTimeout`) | | HandshakeAuth | `json.RawMessage` | Raw JSON auth payload sent by the client at connect time (nil if absent) | | IsAlive | `bool` | Reports whether the underlying connection is still open and the heartbeat loop is running | | IsPolling | `bool` | Reports whether the session is bound to HTTP long-polling rather than WebSocket; when true, `Conn` is nil | | Close | `void` | Actively close the connection from the server | **Note: the FastHTTP connection can be accessed directly from the instance** ```go kws.Conn ``` `kws.Conn` is `nil` for HTTP long-polling sessions. Code that touches the underlying WebSocket directly should guard with `if kws.Conn != nil` or check the transport via the absence of `kws.Conn`. Listener APIs (`Emit`, `Ack`, `Close`, `Broadcast`, `EmitWithAck`, etc.) work transparently on both transports. --- ## SocketIO Legacy Event Shim Compatibility shim for applications that used older `socketio` releases as a plain WebSocket event bus. Deprecated: import `github.com/gofiber/contrib/v3/websocket/event` directly for new code. The root `github.com/gofiber/contrib/v3/socketio` package is reserved for the Engine.IO / Socket.IO protocol and clients such as `socket.io-client`. ## Migration Preferred import: ```go import "github.com/gofiber/contrib/v3/websocket/event" ``` Temporary compatibility import: ```go import "github.com/gofiber/contrib/v3/socketio/legacy" ``` The shim re-exports the old event-bus surface: ```go legacy.On(legacy.EventMessage, func(ep *legacy.EventPayload) { ep.Kws.Emit([]byte("pong"), legacy.TextMessage) }) app.Get("/ws", legacy.New(func(kws *legacy.Websocket) {})) ``` Set tuning globals such as `PongTimeout`, `SendQueueSize`, or `MaxSendRetry` on `github.com/gofiber/contrib/v3/websocket/event` directly before accepting connections. --- ## Swagger UI > ⚠️ This module was renamed from `gofiber/contrib/swagger` to `swaggerui` to clearly distinguish it from the ported `swaggo` middleware. Update your imports accordingly. ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*swaggerui*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20swaggerui/badge.svg) Swagger UI middleware for [Fiber](https://github.com/gofiber/fiber). This handler serves pre-generated Swagger/OpenAPI specs via the swagger-ui package. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...swaggerui.Config) fiber.Handler ``` ### Installation Swagger is tested on the latests [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the Swagger UI middleware: ```bash go get github.com/gofiber/contrib/v3/swaggerui ``` ### Examples Import the middleware package ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/swaggerui" ) ``` Using the default config: ```go app.Use(swaggerui.New()) ``` Using a custom config: ```go cfg := swaggerui.Config{ BasePath: "/", FilePath: "./docs/swagger.json", Path: "swagger", Title: "Swagger API Docs", } app.Use(swaggerui.New(cfg)) ``` Use program data for Swagger content: ```go cfg := swaggerui.Config{ BasePath: "/", FilePath: "./docs/swagger.json", FileContent: mySwaggerByteSlice, Path: "swagger", Title: "Swagger API Docs", } app.Use(swaggerui.New(cfg)) ``` Using multiple instances of Swagger: ```go // Create Swagger middleware for v1 // // Swagger will be available at: /api/v1/docs app.Use(swaggerui.New(swaggerui.Config{ BasePath: "/api/v1/", FilePath: "./docs/v1/swagger.json", Path: "docs", })) // Create Swagger middleware for a second API version // // Swagger will be available at: /api/v2/docs app.Use(swaggerui.New(swaggerui.Config{ BasePath: "/api/v2/", FilePath: "./docs/v2/swagger.json", Path: "docs", })) ``` ### Config ```go type Config struct { // Next defines a function to skip this middleware when returned true. // // Optional. Default: nil Next func(c fiber.Ctx) bool // BasePath for the UI path // // Optional. Default: / BasePath string // FilePath for the swagger.json or swagger.yaml file // // Optional. Default: ./swagger.json FilePath string // FileContent for the content of the swagger.json or swagger.yaml file. // If provided, FilePath will not be read. // // Optional. Default: nil FileContent []byte // Path combines with BasePath for the full UI path // // Optional. Default: docs Path string // Title for the documentation site // // Optional. Default: Fiber API documentation Title string // CacheAge defines the max-age for the Cache-Control header in seconds. // // Optional. Default: 3600 (1 hour) CacheAge int } ``` ### Default Config ```go var ConfigDefault = Config{ Next: nil, BasePath: "/", FilePath: "./swagger.json", Path: "docs", Title: "Fiber API documentation", CacheAge: 3600, // Default to 1 hour } ``` --- ## Swaggo > ⚠️ This module was renamed from `gofiber/swagger` to `swaggo` to clearly distinguish it from the ported `swaggerui` middleware. Update your imports accordingly. ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*swaggo*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/actions/workflows/test-swaggo.yml/badge.svg) Swaggo replaces the archived [github.com/gofiber/swagger](https://github.com/gofiber/swagger) module with an actively maintained drop-in generator for [Fiber](https://github.com/gofiber/fiber) v3. It mounts the official Swagger UI, serves the assets required by [swaggo/swag](https://github.com/swaggo/swag) generated documentation, and exposes helper utilities to wire the docs into any Fiber application. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Usage](#usage) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go var HandlerDefault = New() func New(config ...Config) fiber.Handler ``` ### Installation Swagger Doc Generator is tested on the latest [Go versions](https://go.dev/dl/) with support for modules. Make sure to initialize one first if you have not done that yet: ```bash go mod init github.com// ``` Then install the middleware: ```bash go get github.com/gofiber/contrib/v3/swaggo ``` ### Usage First, document your API using swaggo/swag comments and generate the documentation files (usually inside a `docs` package) by running `swag init`. ```go package main import ( "github.com/gofiber/fiber/v3" swaggo "github.com/gofiber/contrib/v3/swaggo" // docs are generated by Swag CLI, you have to import them. // Replace with your own docs folder, usually "github.com/username/reponame/docs". _ "github.com/username/reponame/docs" ) func main() { app := fiber.New() // Mount the UI with the default configuration under /swagger app.Get("/swagger/*", swaggo.HandlerDefault) // Customize the UI by passing a Config app.Get("/docs/*", swaggo.New(swaggo.Config{ URL: "http://example.com/doc.json", DeepLinking: false, DocExpansion: "none", OAuth2RedirectUrl: "http://localhost:8080/swagger/oauth2-redirect.html", })) app.Listen(":8080") } ``` ### Config ```go type Config struct { InstanceName string Title string ConfigURL string URL string QueryConfigEnabled bool Layout string Plugins []template.JS Presets []template.JS DeepLinking bool DisplayOperationId bool DefaultModelsExpandDepth int DefaultModelExpandDepth int DefaultModelRendering string DisplayRequestDuration bool DocExpansion string Filter FilterConfig MaxDisplayedTags int ShowExtensions bool ShowCommonExtensions bool TagsSorter template.JS OnComplete template.JS SyntaxHighlight *SyntaxHighlightConfig TryItOutEnabled bool RequestSnippetsEnabled bool OAuth2RedirectUrl string RequestInterceptor template.JS RequestCurlOptions []string ResponseInterceptor template.JS ShowMutatedRequest bool SupportedSubmitMethods []string ValidatorUrl string WithCredentials bool ModelPropertyMacro template.JS ParameterMacro template.JS PersistAuthorization bool OAuth *OAuthConfig PreauthorizeBasic template.JS PreauthorizeApiKey template.JS CustomStyle template.CSS CustomScript template.JS } ``` ### Default Config ```go var ConfigDefault = Config{ Title: "Swagger UI", Layout: "StandaloneLayout", URL: "doc.json", DeepLinking: true, ShowMutatedRequest: true, Plugins: []template.JS{ template.JS("SwaggerUIBundle.plugins.DownloadUrl"), }, Presets: []template.JS{ template.JS("SwaggerUIBundle.presets.apis"), template.JS("SwaggerUIStandalonePreset"), }, SyntaxHighlight: &SyntaxHighlightConfig{Activate: true, Theme: "agate"}, } ``` > Refer to `config.go` for a complete list of options and documentation strings. --- ## Testcontainers ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*testcontainers*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Testcontainers%20Services/badge.svg) A [Testcontainers](https://golang.testcontainers.org/) Service Implementation for Fiber. :::note Requires Go **1.25** and above ::: **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. > Test requirement: integration tests for this package require a reachable Docker daemon. ## Common Use Cases - Local development - Integration testing - Isolated service testing - End-to-end testing ## Install :::caution This Service Implementation only supports Fiber **v3**. ::: ```shell go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/testcontainers ``` ## Signature ### NewModuleConfig ```go // NewModuleConfig creates a new container service config for a module. // // - The serviceKey is the key used to identify the service in the Fiber app's state. // - The img is the image name to use for the container. // - The run is the function to use to run the container. It's usually the Run function from the module, like [redis.Run] or [postgres.Run]. // - The opts are the functional options to pass to the run function. This argument is optional. func NewModuleConfig[T testcontainers.Container]( serviceKey string, img string, run func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error), opts ...testcontainers.ContainerCustomizer, ) Config[T] { ``` ### NewContainerConfig ```go // NewContainerConfig creates a new container service config for a generic container type, // not created by a Testcontainers module. So this function best used in combination with // the [AddService] function to add a custom container to the Fiber app's state. // // - The serviceKey is the key used to identify the service in the Fiber app's state. // - The img is the image name to use for the container. // - The opts are the functional options to pass to the [testcontainers.Run] function. This argument is optional. // // This function uses the [testcontainers.Run] function as the run function. func NewContainerConfig[T *testcontainers.DockerContainer](serviceKey string, img string, opts ...testcontainers.ContainerCustomizer) Config[*testcontainers.DockerContainer] ``` ### AddService ```go // AddService adds a Testcontainers container as a [fiber.Service] for the Fiber app. // It returns a pointer to a [ContainerService[T]] object, which contains the key used to identify // the service in the Fiber app's state, and an error if the config is nil. // The container should be a function like redis.Run or postgres.Run that returns a container type // which embeds [testcontainers.Container]. // - The cfg is the Fiber app's configuration, needed to add the service to the Fiber app's state. // - The containerConfig is the configuration for the container, where: // - The containerConfig.ServiceKey is the key used to identify the service in the Fiber app's state. // - The containerConfig.Run is the function to use to run the container. It's usually the Run function from the module, like redis.Run or postgres.Run. // - The containerConfig.Image is the image to use for the container. // - The containerConfig.Options are the functional options to pass to the [testcontainers.Run] function. This argument is optional. // // Use [NewModuleConfig] or [NewContainerConfig] helper functions to create valid containerConfig objects. func AddService[T testcontainers.Container](cfg *fiber.Config, containerConfig Config[T]) (*ContainerService[T], error) { ``` ## Types ### Config The `Config` type is a generic type that is used to configure the container. | Property | Type | Description | Default | |-------------|------|-------------|---------| | ServiceKey | string | The key used to identify the service in the Fiber app's state. | - | | Image | string | The image name to use for the container. | - | | Run | func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error) | The function to use to run the container. It's usually the Run function from the testcontainers-go module, like redis.Run or postgres.Run | - | | Options | []testcontainers.ContainerCustomizer | The functional options to pass to the [testcontainers.Run] function. This argument is optional. | - | ```go // Config contains the configuration for a container service. type Config[T testcontainers.Container] struct { // ServiceKey is the key used to identify the service in the Fiber app's state. ServiceKey string // Image is the image name to use for the container. Image string // Run is the function to use to run the container. // It's usually the Run function from the testcontainers-go module, like redis.Run or postgres.Run, // although it could be the generic [testcontainers.Run] function from the testcontainers-go package. Run func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error) // Options are the functional options to pass to the [testcontainers.Run] function. This argument is optional. // You can find the available options in the [testcontainers website]. // // [testcontainers website]: https://golang.testcontainers.org/features/creating_container/#customizing-the-container Options []testcontainers.ContainerCustomizer } ``` ### ContainerService The `ContainerService` type is a generic type that embeds a [testcontainers.Container](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#Container) interface, and implements the [fiber.Service] interface, thanks to the Start, String, State and Terminate methods. It manages the lifecycle of a `testcontainers.Container` instance, and it can be retrieved from the Fiber app's state calling the `fiber.MustGetService` function with the key returned by the `ContainerService.Key` method. The type parameter `T` must implement the [testcontainers.Container](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#Container) interface, as in the Testcontainers Go modules (e.g. [redis.RedisContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go/modules/redis#RedisContainer), [postgres.PostgresContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go/modules/postgres#PostgresContainer), etc.), or in the generic [testcontainers.DockerContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#GenericContainer) type, used for custom containers. :::note Since `ContainerService` implements the `fiber.Service` interface, container cleanup is handled automatically by the Fiber framework when the application shuts down. There's no need for manual cleanup code. ::: ```go type ContainerService[T testcontainers.Container] struct ``` #### Signature #####  Key ```go // Key returns the key used to identify the service in the Fiber app's state. // Consumers should use string constants for service keys to ensure consistency // when retrieving services from the Fiber app's state. func (c *ContainerService[T]) Key() string ``` ##### Container ```go // Container returns the Testcontainers container instance, giving full access to the T type methods. // It's useful to access the container's methods, like [testcontainers.Container.MappedPort] // or [testcontainers.Container.Inspect]. func (c *ContainerService[T]) Container() T ``` ##### Start ```go // Start creates and starts the container, calling the [run] function with the [img] and [opts] arguments. // It implements the [fiber.Service] interface. func (c *ContainerService[T]) Start(ctx context.Context) error ``` ##### String ```go // String returns the service key, which uniquely identifies the container service. // It implements the [fiber.Service] interface. func (c *ContainerService[T]) String() string ``` ##### State ```go // State returns the status of the container. // It implements the [fiber.Service] interface. func (c *ContainerService[T]) State(ctx context.Context) (string, error) ``` ##### Terminate ```go // Terminate stops and removes the container. It implements the [fiber.Service] interface. func (c *ContainerService[T]) Terminate(ctx context.Context) error ``` ### Common Errors | Error | Description | Resolution | |-------|-------------|------------| | ErrNilConfig | Returned when the config is nil | Ensure config is properly initialized | | ErrContainerNotRunning | Returned when the container is not running | Check container state before operations | | ErrEmptyServiceKey | Returned when the service key is empty | Provide a non-empty service key | | ErrImageEmpty | Returned when the image is empty | Provide a valid image name | | ErrRunNil | Returned when the run is nil | Provide a valid run function | ## Examples You can find more examples in the [testable examples](https://github.com/gofiber/contrib/blob/main/v3/testcontainers/examples_test.go). ### Adding a module container using the Testcontainers Go's Redis module ```go package main import ( "fmt" "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/testcontainers" tc "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/redis" ) func main() { cfg := &fiber.Config{} // Define the base key for the module service. // The service returned by the [testcontainers.AddService] function, // using the [ContainerService.Key] method, // concatenates the base key with the "using testcontainers-go" suffix. const ( redisKey = "redis-module" ) // Adding containers coming from the testcontainers-go modules, // in this case, a Redis and a Postgres container. redisModuleConfig := testcontainers.NewModuleConfig(redisKey, "redis:latest", redis.Run) redisSrv, err := testcontainers.AddService(cfg, redisModuleConfig) if err != nil { log.Println("error adding redis module:", err) return } // Create a new Fiber app, using the provided configuration. app := fiber.New(*cfg) // Retrieve all services from the app's state. // This returns a slice of all the services registered in the app's state. srvs := app.State().Services() // Retrieve the Redis container from the app's state using the key returned by the [ContainerService.Key] method. redisCtr := fiber.MustGetService[*testcontainers.ContainerService[*redis.RedisContainer]](app.State(), redisSrv.Key()) // Start the Fiber app. app.Listen(":3000") } ``` ### Adding a custom container using the Testcontainers Go package ```go package main import ( "fmt" "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/testcontainers" tc "github.com/testcontainers/testcontainers-go" ) func main() { cfg := &fiber.Config{} // Define the base key for the generic service. // The service returned by the [testcontainers.AddService] function, // using the [ContainerService.Key] method, // concatenates the base key with the "using testcontainers-go" suffix. const ( nginxKey = "nginx-generic" ) // Adding a generic container, directly from the testcontainers-go package. containerConfig := testcontainers.NewContainerConfig(nginxKey, "nginx:latest", tc.WithExposedPorts("80/tcp")) nginxSrv, err := testcontainers.AddService(cfg, containerConfig) if err != nil { log.Println("error adding nginx generic:", err) return } app := fiber.New(*cfg) nginxCtr := fiber.MustGetService[*testcontainers.ContainerService[*tc.DockerContainer]](app.State(), nginxSrv.Key()) // Start the Fiber app. app.Listen(":3000") } ``` --- ## Uptime ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*uptime*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20Uptime/badge.svg) Uptime middleware for [Fiber](https://github.com/gofiber/fiber) that records in-process heartbeat history and serves a lightweight status page. **Compatible with Fiber v3.** ## Preview ![Uptime dashboard preview](https://raw.githubusercontent.com/gofurry/images/refs/heads/main/github/uptime/preview.png) ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/uptime go get -u github.com/gofiber/storage/redis/v3 ``` ## Signature ```go uptime.New(config ...uptime.Config) fiber.Handler uptime.RemoveService(ctx context.Context, store *fiberredis.Storage, keyPrefix, serviceID string) error ``` ## Basic usage ```go package main import ( "github.com/gofiber/contrib/v3/uptime" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" fiberredis "github.com/gofiber/storage/redis/v3" ) func main() { app := fiber.New() store := fiberredis.New() app.Hooks().OnPostShutdown(func(_ error) error { return store.Close() }) app.Use(uptime.New(uptime.Config{ App: app, Store: store, ServiceID: "api", ServiceName: "API", })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("ok") }) log.Fatal(app.Listen(":3000")) } ``` Open: - `http://localhost:3000/uptime` - `http://localhost:3000/uptime/api/status` ## Endpoint probes By default, uptime records a heartbeat for the current process identified by `ServiceID`. You can also configure HTTP endpoints. Each endpoint is shown as a separate service on the dashboard and in the JSON API. ```go app.Use(uptime.New(uptime.Config{ App: app, Store: store, Endpoints: []uptime.EndpointConfig{ { ID: "api-health", Name: "API Health", URL: "https://api.example.com/health", Method: "GET", Interval: 10 * time.Second, Timeout: 3 * time.Second, ExpectedStatusCodes: []int{200, 204}, }, }, })) ``` When `ExpectedStatusCodes` is empty, any `2xx` or `3xx` response is considered up. Failed probes do not write heartbeat slots, so the endpoint naturally moves to yellow, red, or down as slots are missed. Every instance runs its own probes, so N replicas sharing a `StorageKeyPrefix` send N times the probe traffic to each target. A slot counts as up when any replica saw the endpoint up. The first probe runs when `New` is called, before `app.Listen` has opened the port. Endpoints pointing at the app's own routes therefore miss their first slot or two on startup. ## Redis Redis state is stored through `github.com/gofiber/storage/redis/v3`. Create the Fiber Redis storage in your app and pass it to the uptime config. By default, that storage package connects to `127.0.0.1:6379` and database `0`. ```go store := fiberredis.New(fiberredis.Config{ Addrs: []string{"127.0.0.1:6379"}, Password: "", Database: 0, }) app.Hooks().OnPostShutdown(func(_ error) error { return store.Close() }) app.Use(uptime.New(uptime.Config{ App: app, Store: store, ServiceID: "api", StorageKeyPrefix: "fiber:uptime", })) ``` The middleware uses `Conn()` from the Fiber Redis storage to run the uptime queries it needs. Use a dedicated Redis database or a distinct `StorageKeyPrefix` when multiple environments share the same Redis server. The caller owns the Redis storage lifecycle. Close the Redis storage after the Fiber app has shut down, for example from `OnPostShutdown`, so uptime can stop its background recorders first. When multiple uptime instances share the same Redis database and `StorageKeyPrefix`, use the same `Timezone` and heartbeat or probe interval for the same `ServiceID` or endpoint `ID`. If those settings need to differ, use distinct service IDs, endpoint IDs, or storage prefixes. ### Key expiry Service, sample and daily keys carry a TTL of `RetentionDays + 1` days that is re-armed on every write. Background maintenance normally removes them long before that, so the TTL only matters once no process is left to run the cleanup: a decommissioned service, a changed key prefix, an app that never comes back. Without it those keys would stay in Redis forever. The `services` registry set is shared by all instances under a prefix and is never expired. A service that has never recorded a successful heartbeat also keeps its (small) service hash without a TTL. Use `RemoveService` to clear either. ## Removing a service The dashboard lists every service found under `StorageKeyPrefix`, not just the ones in the current config. Services are never dropped automatically while their retention window keeps being refreshed, so renaming `ServiceID` or an endpoint `ID`, or removing an endpoint, leaves the old identifier behind as a row that reports down forever. Delete it explicitly: ```go err := uptime.RemoveService(context.Background(), store, "fiber:uptime", "old-endpoint-id") ``` Pass the same `StorageKeyPrefix` the middleware uses, or `""` for the default. This removes the service, its history and its raw samples. Remove the service from your config and restart before calling it. Calling it while the service is still being recorded takes it off the dashboard for good, but in-flight heartbeats will recreate partial keys; those carry the TTL above and expire on their own. ## Snapshots and custom UI The dashboard and JSON API build a fresh `Snapshot` from the backing store on each request. Use Fiber's cache middleware around the uptime route if you want HTTP-level caching. The same snapshot payload is available at `UI.Path + "/api/status"` for custom dashboards. ## Dashboard favicon The built-in dashboard includes an embedded favicon by default. Set `UI.FaviconURL` to override it with either a root-relative path served by the same application or an absolute HTTP(S) URL: ```go app.Use(uptime.New(uptime.Config{ App: app, Store: store, ServiceID: "api", UI: uptime.UIConfig{ FaviconURL: "/assets/favicon.svg", }, })) ``` Filesystem paths such as `./favicon.ico` are not supported directly. Expose a local file through a Fiber route or static handler, then configure its URL. Remote favicon URLs cause each dashboard visitor's browser to contact that remote host, so a same-origin URL is preferred for private deployments. ## Config | Property | Type | Description | Default | |:--|:--|:--|:--| | App | `*fiber.App` | Fiber app used to register the shutdown hook that closes the uptime runtime. | Required | | Next | `func(fiber.Ctx) bool` | Skip the uptime handler when true. | `nil` | | ServiceID | `string` | Stable service identifier for the current process. Required only when `Endpoints` is empty. | `""` | | ServiceName | `string` | Display name. | `ServiceID` | | ServiceDescription | `string` | Display description. | `""` | | Endpoints | `[]uptime.EndpointConfig` | Optional HTTP endpoints to probe as tracked services. | `nil` | | SampleInterval | `time.Duration` | Heartbeat interval. | `3 * time.Second` | | RetentionDays | `int` | Number of days to retain daily history. Also sets the key expiry backstop. | `90` | | DaysToShow | `int` | Number of days shown in snapshots and dashboard. | `30` | | Timezone | `*time.Location` | Timezone for day and slot boundaries. | `time.Local` | | NodeID | `int64` | Optional node value used for generated instance IDs. | `0` | | InstanceID | `int64` | Explicit process instance ID. | Generated | | IDGenerator | `uptime.IDGenerator` | Custom instance ID generator. | `nil` | | Store | `*fiberredis.Storage` | Fiber Redis storage instance from `github.com/gofiber/storage/redis/v3`. | Required | | StorageKeyPrefix | `string` | Prefix for all uptime Redis keys. | `"fiber:uptime"` | | UI | `uptime.UIConfig` | Dashboard copy, favicon, and thresholds. `FaviconURL` accepts a root-relative path or absolute HTTP(S) URL. Threshold values are configurable in `(0, 1]`; zero uses the defaults. | Embedded favicon, light English UI, green at `99.9%`, yellow at `99%` | ### EndpointConfig | Property | Type | Description | Default | |:--|:--|:--|:--| | ID | `string` | Stable endpoint identifier. | Required | | Name | `string` | Display name. | `ID` | | Description | `string` | Display description. | `""` | | URL | `string` | Absolute `http` or `https` URL to probe. | Required | | Method | `string` | HTTP method used for the probe. | `GET` | | Headers | `map[string]string` | Optional request headers sent with each probe. | `nil` | | ExpectedStatusCodes | `[]int` | Status codes that mark the endpoint up. Empty means any `2xx` or `3xx`. | `nil` | | Interval | `time.Duration` | Endpoint heartbeat interval. | `Config.SampleInterval` | | Timeout | `time.Duration` | Maximum duration for one probe. | `5 * time.Second` | ## Handler behavior The Fiber handler serves: - `/uptime` - `/uptime/` - `/uptime/api/status` `GET` and `HEAD` are supported. Other methods return `405 Method Not Allowed`. Requests outside `UI.Path`, including unknown uptime subpaths, are passed to the next handler. The handler matches request paths against `UI.Path` (default `/uptime`). When mounted under a Fiber group or `Use` prefix, the match is relative to that mount point. The middleware does not read request bodies, capture response bodies, or wrap business handlers. Process heartbeats and endpoint probes are run by background tickers owned by the uptime runtime. ## Performance notes Store writes and endpoint probes happen on background tickers, not on every business request. Status requests read the backing store to build the response. Use Fiber's cache middleware if the dashboard or JSON API should be cached. ## Concurrency safety Uptime middleware instances are safe for concurrent use after construction. The snapshot payload is built from fresh store reads. Redis commands are issued through the Fiber Redis storage connection and are safe for concurrent use by the background recorder and Fiber handlers. `Config.App` is required so `New` can register a Fiber shutdown hook that stops the uptime runtime. The caller owns the Redis storage lifecycle and should close it after the app has shut down, for example from `OnPostShutdown` or a signal handler that closes Redis after `app.Shutdown` returns. ## Security notes Mount the dashboard on an internal or protected route when uptime history should not be public. The middleware does not log request bodies, response bodies, authorization headers, cookies, or query strings. Endpoint probe response bodies are closed without being read. Avoid putting secrets in endpoint URLs because URLs may still appear in upstream infrastructure logs outside this middleware. --- ## Websocket ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*websocket*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20websocket/badge.svg) Based on [Fasthttp WebSocket](https://github.com/fasthttp/websocket) for [Fiber](https://github.com/gofiber/fiber) with available `fiber.Ctx` methods like [Locals](http://docs.gofiber.io/ctx#locals), [Params](http://docs.gofiber.io/ctx#params), [Query](http://docs.gofiber.io/ctx#query) and [Cookies](http://docs.gofiber.io/ctx#cookies). For a plain WebSocket event-bus helper, use the [`event`](./event/README.md) subpackage. It keeps ordinary WebSocket wire compatibility and is separate from the Socket.IO protocol implementation in `github.com/gofiber/contrib/v3/socketio`. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/websocket ``` ## Signatures ```go func New(handler func(*websocket.Conn), config ...websocket.Config) fiber.Handler { ``` ## Config | Property | Type | Description | Default | |:--------------------|:-----------------------------|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------| | Next | `func(fiber.Ctx) bool` | Defines a function to skip this middleware when it returns true. | `nil` | | HandshakeTimeout | `time.Duration` | HandshakeTimeout specifies the duration for the handshake to complete. | `0` (No timeout) | | Subprotocols | `[]string` | Subprotocols specifies the client's requested subprotocols. | `nil` | | Origins | `[]string` | Allowed Origins based on the Origin header. If empty, everything is allowed. | `nil` | | AllowEmptyOrigin | `bool` | Allows connections without an Origin header when Origins is configured. Useful for non-browser clients. | `false` | | ReadBufferSize | `int` | ReadBufferSize specifies the I/O buffer size in bytes for incoming messages. | `0` (Use default size) | | WriteBufferSize | `int` | WriteBufferSize specifies the I/O buffer size in bytes for outgoing messages. | `0` (Use default size) | | WriteBufferPool | `websocket.BufferPool` | WriteBufferPool is a pool of buffers for write operations. | `nil` | | EnableCompression | `bool` | EnableCompression specifies if the client should attempt to negotiate per message compression (RFC 7692). | `false` | | RecoverHandler | `func(*websocket.Conn)` | RecoverHandler is a panic handler function that recovers from panics. | `defaultRecover` | ## Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/websocket" ) func main() { app := fiber.New() app.Use("/ws", func(c fiber.Ctx) error { // IsWebSocketUpgrade returns true if the client // requested upgrade to the WebSocket protocol. if websocket.IsWebSocketUpgrade(c) { c.Locals("allowed", true) return c.Next() } return fiber.ErrUpgradeRequired }) app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) { // c.Locals is added to the *websocket.Conn log.Println(c.Locals("allowed")) // true log.Println(c.Params("id")) // 123 log.Println(c.Query("v")) // 1.0 log.Println(c.Cookies("session")) // "" // websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index var ( mt int msg []byte err error ) for { if mt, msg, err = c.ReadMessage(); err != nil { log.Println("read:", err) break } log.Printf("recv: %s", msg) if err = c.WriteMessage(mt, msg); err != nil { log.Println("write:", err) break } } })) log.Fatal(app.Listen(":3000")) // Access the websocket server: ws://localhost:3000/ws/123?v=1.0 // https://www.websocket.org/echo.html } ``` ## Note with cache middleware If you get the error `websocket: bad handshake` when using the [cache middleware](https://github.com/gofiber/fiber/tree/master/middleware/cache), please use `config.Next` to skip websocket path. ```go app := fiber.New() app.Use(cache.New(cache.Config{ Next: func(c fiber.Ctx) bool { return strings.Contains(c.Route().Path, "/ws") }, })) app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {})) ``` ## Note with recover middleware For internal implementation reasons, currently recover middleware does not work with websocket middleware, please use `config.RecoverHandler` to add recover handler to websocket endpoints. By default, config `RecoverHandler` recovers from panic and writes stack trace to stderr, also returns a response that contains panic message in **error** field. ```go app := fiber.New() app.Use(cache.New(cache.Config{ Next: func(c fiber.Ctx) bool { return strings.Contains(c.Route().Path, "/ws") }, })) cfg := Config{ RecoverHandler: func(conn *Conn) { if err := recover(); err != nil { conn.WriteJSON(fiber.Map{"customError": "error occurred"}) } }, } app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {}, cfg)) ``` ## Note for WebSocket subprotocols The config `Subprotocols` only helps you negotiate subprotocols and sets a `Sec-Websocket-Protocol` header if it has a suitable subprotocol. For more about negotiates process, check the comment for `Subprotocols` in [fasthttp.Upgrader](https://pkg.go.dev/github.com/fasthttp/websocket#Upgrader) . All connections will be sent to the handler function no matter whether the subprotocol negotiation is successful or not. You can get the selected subprotocol from `conn.Subprotocol()`. If a connection includes the `Sec-Websocket-Protocol` header in the request but the protocol negotiation fails, the browser will immediately disconnect the connection after receiving the upgrade response. --- ## WebSocket Event Plain WebSocket event helper for [Fiber](https://github.com/gofiber/fiber), built on top of `github.com/gofiber/contrib/v3/websocket`. This package is for applications that want the legacy event-bus behavior over ordinary WebSocket clients. It does not implement the Engine.IO or Socket.IO protocol. Use `github.com/gofiber/contrib/v3/socketio` when you need compatibility with the official `socket.io-client` package. If your application used the older `socketio` package as a plain WebSocket event bus, migrate the import to `github.com/gofiber/contrib/v3/websocket/event`. The API intentionally stays close to that legacy event helper, while the `socketio` package is reserved for the Socket.IO protocol. **Compatible with Fiber v3.** ## Install ```sh go get -u github.com/gofiber/contrib/v3/websocket ``` The event helper is the `event` subpackage of that module: ```go import "github.com/gofiber/contrib/v3/websocket/event" ``` ## Signatures Create a handler: ```go func New(callback func(kws *event.Websocket), config ...websocket.Config) fiber.Handler func NewWithConfig(callback func(kws *event.Websocket), eventCfg event.Config, wsConfig ...websocket.Config) fiber.Handler ``` Register listeners. Package-level `On` is process-global; the `(*Websocket)` method form is scoped to a single connection (see [Listeners](#listeners)): ```go type EventCallback func(payload *event.EventPayload) func On(name string, callback EventCallback) // global: fires for every connection func Off(name string) // removes global listeners for name func (kws *Websocket) On(name string, callback EventCallback) // this connection only func (kws *Websocket) Off(name string) // remove this connection's listeners ``` Send messages. The `(*Websocket)` method forms operate on / from a specific connection and fire `EventError` on failure; the package-level forms address connections in the global pool and do not fire `EventError` (see [Sending messages](#sending-messages)): ```go // Method forms (fire EventError on failure). func (kws *Websocket) Emit(message []byte, mType ...int) func (kws *Websocket) EmitTo(uuid string, message []byte, mType ...int) error func (kws *Websocket) EmitToList(uuids []string, message []byte, mType ...int) func (kws *Websocket) Broadcast(message []byte, except bool, mType ...int) func (kws *Websocket) Fire(name string, data []byte) // Package forms (do not fire EventError). func EmitTo(uuid string, message []byte, mType ...int) error func EmitToList(uuids []string, message []byte, mType ...int) func Broadcast(message []byte, mType ...int) func Fire(name string, data []byte) ``` Connection identity and attributes: ```go func (kws *Websocket) GetUUID() string func (kws *Websocket) SetUUID(uuid string) error // returns ErrorUUIDDuplication on conflict func (kws *Websocket) SetAttribute(key string, value interface{}) func (kws *Websocket) GetAttribute(key string) interface{} ``` Graceful shutdown: ```go func Drain() func IsDraining() bool func CloseAll(ctx context.Context, code int, reason string) error ``` ## Listeners `On` registers a **process-global** listener: the callback fires for the given event on **every** connection created by `New` / `NewWithConfig`, regardless of route or `Config`. Listeners are additive and stay registered until removed with `Off`. This matches the legacy `socketio` event bus. ```go event.On(event.EventMessage, func(ep *event.EventPayload) { /* ... */ }) event.Off(event.EventMessage) // remove again, e.g. on reconfiguration or in tests ``` For listeners that should fire for a single connection only, use the `(*Websocket).On` method (typically from the `New` callback). Per-connection listeners fire in addition to the global ones and are discarded automatically when the connection disconnects: ```go app.Get("/ws/:id", event.New(func(kws *event.Websocket) { kws.On(event.EventMessage, func(ep *event.EventPayload) { // only fires for this connection }) })) ``` ## Sending messages There are two flavors of `EmitTo` / `EmitToList` / `Broadcast`, and the difference is easy to miss: | Form | Targets | On failure | |:-----|:--------|:-----------| | `(*Websocket).EmitTo` | a UUID in the pool | fires `EventError` on `kws` and returns the error | | `(*Websocket).EmitToList` | a list of UUIDs | fires `EventError` on `kws` per failed UUID | | `(*Websocket).Broadcast` | all connections (`except` skips `kws` itself) | fires `EventError` on `kws` per failed UUID | | `EmitTo` (package) | a UUID in the pool | returns the error, does **not** fire `EventError` | | `EmitToList` (package) | a list of UUIDs | silently ignores per-UUID errors | | `Broadcast` (package) | all connections | fire-and-forget, no error feedback | Use the method forms when you want delivery failures surfaced as `EventError` events; use the package forms for fire-and-forget fan-out. `Emit` enqueues the message on the connection's outbound queue, which a dedicated goroutine drains in order; if the queue is full it blocks until a slot frees up or the connection closes. ## Configuration Per-instance tuning via `event.Config` passed to `NewWithConfig`. Zero values fall back to the matching package-level var, which itself falls back to the hard default. | Config field | Default | Description | |:--------------------|:--------|:------------| | `PingInterval` | `1s` | Interval between server-originated Ping frames. Must be less than any upstream proxy or load balancer idle timeout. | | `ReadIdleTimeout` | `3 * PingInterval` | Maximum silence before the read deadline fires and the connection is disconnected. | | `WriteTimeout` | `10s` | Bounds a single `WriteMessage` / `WriteControl` call. | | `MaxMessageSize` | `1 MiB` | Inbound frame size limit. Set to `math.MaxInt64` to opt out. | | `SendQueueSize` | `100` | Per-connection outbound message queue capacity. | | `MaxSendRetry` | `5` | Max retries for transient socket write readiness issues. | | `RetrySendTimeout` | `20ms` | Backoff between retries while the connection is not ready. | | `RecoverHandler` | `nil` | Called on a panic inside a user `On` callback. If `nil`, panics are recovered silently. | The legacy package-level vars (`PongTimeout`, `RetrySendTimeout`, `MaxSendRetry`, `SendQueueSize`, `ReadTimeout`) are still read once per connection at upgrade time for backwards compatibility, but mutating them after a connection is established has no effect on running goroutines. Prefer `NewWithConfig` for new code. ## Thread safety `On`, `Off`, `Fire`, the package-level `EmitTo` / `EmitToList` / `Broadcast`, and the connection pool are safe for concurrent use. A single `*Websocket` is safe to use from multiple goroutines: reads and writes are serialized through a per-connection mutex and an outbound send queue. Listener callbacks run on the helper's goroutines, so a callback that blocks holds up event delivery for that connection; offload long work to your own goroutine. ## Graceful Shutdown The helper keeps an in-process pool of active connections. Use `event.Drain` and `event.CloseAll` together with a Fiber shutdown hook so clients receive a clean `1001 Going Away` close frame instead of an abrupt TCP reset: ```go app.Hooks().OnShutdown(func() error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() event.Drain() return event.CloseAll(ctx, websocket.CloseGoingAway, "server shutting down") }) ``` If `ctx` expires before every goroutine exits, `CloseAll` force-closes the remaining underlying connections and returns `ctx.Err()`. `Drain` only flips the draining flag; it does **not** refuse new connections by itself. Gate the upgrade route on `IsDraining` to stop accepting clients during shutdown: ```go app.Use("/ws", func(c fiber.Ctx) error { if event.IsDraining() { return fiber.NewError(fiber.StatusServiceUnavailable, "shutting down") } if websocket.IsWebSocketUpgrade(c) { return c.Next() } return fiber.ErrUpgradeRequired }) ``` ## Example ```go package main import ( "log" "github.com/gofiber/contrib/v3/websocket" "github.com/gofiber/contrib/v3/websocket/event" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Use("/ws", func(c fiber.Ctx) error { if websocket.IsWebSocketUpgrade(c) { return c.Next() } return fiber.ErrUpgradeRequired }) event.On(event.EventMessage, func(ep *event.EventPayload) { ep.Kws.Emit([]byte("echo: "+string(ep.Data)), event.TextMessage) }) app.Get("/ws/:id", event.New(func(kws *event.Websocket) { // kws.Params / kws.Locals / kws.Query / kws.Cookies wrap the Fiber // request context captured at upgrade time. kws.SetAttribute("user_id", kws.Params("id")) })) log.Fatal(app.Listen(":3000")) } ``` ### Custom events Event names are arbitrary strings. Register a listener with `On` and trigger it with `Fire` (on one connection) or the package-level `Fire` (on all): ```go event.On("notify", func(ep *event.EventPayload) { log.Printf("notify %s: %s", ep.SocketUUID, ep.Data) }) // from a connection: kws.Fire("notify", []byte("hello")) // to every active connection: event.Fire("notify", []byte("broadcast")) ``` ## Supported Events | Const | Event | Description | |:------------------|:-------------|:--------------------------------------------------------| | `EventMessage` | `message` | Fired when a text or binary message is received. | | `EventPing` | `ping` | Fired when a WebSocket ping control frame is received. | | `EventPong` | `pong` | Fired when a WebSocket pong control frame is received. | | `EventDisconnect` | `disconnect` | Fired when the connection is closed. On an error close, `EventError` fires too. | | `EventConnect` | `connect` | Fired after the `New` callback runs, before the read loop starts. | | `EventClose` | `close` | Fired when the server actively closes the connection. | | `EventError` | `error` | Fired on a failed `EmitTo`, a dropped outbound message, or an error-driven disconnect. | ## Event Payload | Field | Type | Description | |:-------------------|:-----------------------|:-------------------------------------------------| | `Kws` | `*event.Websocket` | The connection object. | | `Name` | `string` | The event name. | | `SocketUUID` | `string` | Unique connection UUID. | | `SocketAttributes` | `map[string]any` | Snapshot of optional connection attributes. | | `Error` | `error` | Optional error for disconnect and error events. | | `Data` | `[]byte` | Data used on message, custom, and error events. | --- ## Zap ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*zap*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20zap/badge.svg) [Zap](https://github.com/uber-go/zap) logging support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/zap go get -u go.uber.org/zap ``` ### Signature ```go zap.New(config ...zap.Config) fiber.Handler ``` ### Config | Property | Type | Description | Default | | :--------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | | Next | `func(fiber.Ctx) bool` | Define a function to skip this middleware when returned true | `nil` | | Logger | `*zap.Logger` | Add custom zap logger. | `zap.NewProduction()` | | Fields | `[]string` | Add fields that you want to see. | `[]string{"latency", "status", "method", "url"}` | | FieldsFunc | `func(fiber.Ctx) []zap.Field` | Define a function to add custom fields. | `nil` | | Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | | Levels | `[]zapcore.Level` | Custom response levels. | `[]zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.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 ```go package main import ( "log" middleware "github.com/gofiber/contrib/v3/zap" "github.com/gofiber/fiber/v3" "go.uber.org/zap" ) func main() { app := fiber.New() logger, _ := zap.NewProduction() defer logger.Sync() app.Use(middleware.New(middleware.Config{ Logger: logger, })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) log.Fatal(app.Listen(":3000")) } ``` ## NewLogger ### Signature ```go zap.NewLogger(config ...zap.LoggerConfig) *zap.LoggerConfig ``` ### LoggerConfig | Property | Type | Description | Default | | :---------- | :------------- | :------------------------------------------------------------------------------------------------------- | :----------------------------- | | CoreConfigs | `[]CoreConfig` | Define Config for zapcore | `zap.LoggerConfigDefault` | | SetLogger | `*zap.Logger` | Add custom zap logger. if not nil, `ZapOptions`, `CoreConfigs`, `SetLevel`, `SetOutput` will be ignored. | `nil` | | ExtraKeys | `[]string` | Allow users log extra values from context. | `[]string{}` | | ZapOptions | `[]zap.Option` | Allow users to configure the zap.Option supplied by zap. | `[]zap.Option{}` | ### Example ```go package main import ( "context" middleware "github.com/gofiber/contrib/v3/zap" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" ) func main() { app := fiber.New() logger := middleware.NewLogger(middleware.LoggerConfig{ ExtraKeys: []string{"request_id"}, }) log.SetLogger(logger) defer logger.Sync() app.Use(func(c fiber.Ctx) error { ctx := context.WithValue(c.Context(), "request_id", "123") c.SetContext(ctx) return c.Next() }) app.Get("/", func(c fiber.Ctx) error { log.WithContext(c.Context()).Info("Hello, World!") return c.SendString("Hello, World!") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Zerolog ![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=*zerolog*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/contrib/workflows/Test%20zerolog/badge.svg) [Zerolog](https://github.com/rs/zerolog/) logging support for Fiber. **Compatible with Fiber v3.** ## Go version support We only support the latest two versions of Go. Visit [https://go.dev/doc/devel/release](https://go.dev/doc/devel/release) for more information. ## Install ```sh go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/contrib/v3/zerolog go get -u github.com/rs/zerolog/log ``` ## Signature ```go zerolog.New(config ...zerolog.Config) fiber.Handler ``` ## Config | Property | Type | Description | Default | |:----------------|:------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------| | Next | `func(fiber.Ctx) bool` | Define a function to skip this middleware when it returns true. | `nil` | | Skip | `func(fiber.Ctx) bool` | Define a function that is called **after** the request has been processed but **before** the log entry is assembled. If it returns true, logging is skipped entirely with zero overhead. This gives access to the full request context, including data set by downstream middlewares. | `nil` | | Logger | `*zerolog.Logger` | Add a custom zerolog logger. | `zerolog.New(os.Stderr).With().Timestamp().Logger()` | | GetLogger | `func(fiber.Ctx) zerolog.Logger` | Get a custom zerolog logger. If set, the returned logger replaces `Logger`. | `nil` | | Fields | `[]string` | Add the fields you want to log. | `[]string{"latency", "status", "method", "url", "error"}` | | SkipField | `func(string, fiber.Ctx) bool` | Skip logging a field when it returns true. | `nil` | | SkipHeader | `func(string, fiber.Ctx) bool` | Skip logging a header when it returns true. | `nil` | | RedactHeader | `func(string, fiber.Ctx) bool` | Replace every value of a header with `[REDACTED]` when it returns true. Common credential-bearing headers such as `Authorization`, `Cookie`, `Set-Cookie`, `Location`, `X-Api-Key`, `X-Auth-Token`, and CSRF-token headers are redacted by default. | built-in sensitive-header filter | | WrapHeaders | `bool` | Wrap headers into a 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 `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}` | | GetResBody | `func(c fiber.Ctx) []byte` | Define a function to get the response body when it returns non-nil.For example, with compress middleware the body can be unreadable; `GetResBody` lets you provide a readable body. | `nil` | ## Example ```go package main import ( "os" middleware "github.com/gofiber/contrib/v3/zerolog" "github.com/gofiber/fiber/v3" "github.com/rs/zerolog" ) func main() { app := fiber.New() logger := zerolog.New(os.Stderr).With().Timestamp().Logger() app.Use(middleware.New(middleware.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") } } ``` --- ## Retry Addon The Retry addon for [Fiber](https://github.com/gofiber/fiber) retries failed network operations using exponential backoff with jitter. It repeatedly invokes a function until it succeeds or the maximum number of attempts is exhausted. Jitter at each step breaks client synchronization and helps avoid collisions. If all attempts fail, the addon returns an error. ## Table of Contents - [Signatures](#signatures) - [Examples](#examples) - [Default Config](#default-config) - [Custom Config](#custom-config) - [Config](#config) - [Default Config Example](#default-config-example) ## Signatures ```go func NewExponentialBackoff(config ...retry.Config) *retry.ExponentialBackoff ``` ## Examples ```go 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 used inside Retry var resp *client.Response var err error // Retry a network request and return an error to signal another attempt 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 200 OK") } 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 ```go retry.NewExponentialBackoff() ``` ## Custom Config ```go retry.NewExponentialBackoff(retry.Config{ InitialInterval: 2 * time.Second, MaxBackoffTime: 64 * time.Second, Multiplier: 2.0, MaxRetryCount: 15, }) ``` ## Config ```go // 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 // currentInterval tracks the current waiting time. // // Optional. Default: 1 * time.Second currentInterval time.Duration } ``` ## Default Config Example ```go // 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, } ``` --- ## 🚀 App Use the index to jump straight to any `App` method; filter by name or by category: ## Routing ### Route Handlers Registers a route bound to a specific [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). The canonical handler is `func(fiber.Ctx) error`; Fiber also accepts `func(fiber.Ctx)` and runs it as if it returned `nil`. ```go title="Signatures" // HTTP methods func (app *App) Get(path string, handler any, handlers ...any) Router func (app *App) Head(path string, handler any, handlers ...any) Router func (app *App) Post(path string, handler any, handlers ...any) Router func (app *App) Put(path string, handler any, handlers ...any) Router func (app *App) Delete(path string, handler any, handlers ...any) Router func (app *App) Connect(path string, handler any, handlers ...any) Router func (app *App) Options(path string, handler any, handlers ...any) Router func (app *App) Trace(path string, handler any, handlers ...any) Router func (app *App) Patch(path string, handler any, handlers ...any) Router func (app *App) Query(path string, handler any, handlers ...any) Router // Add registers the same handlers on multiple methods at once. // The handlers run in order, starting with `handler` and then the variadic `handlers`. func (app *App) Add(methods []string, path string, handler any, handlers ...any) Router // All registers the route on every HTTP method at the EXACT path // (unlike Use, which is prefix-matched). func (app *App) All(path string, handler any, handlers ...any) Router ``` ```go title="Examples" // Simple GET handler app.Get("/api/list", func(c fiber.Ctx) error { return c.SendString("I'm a GET request!") }) // Simple POST handler app.Post("/api/register", func(c fiber.Ctx) error { return c.SendString("I'm a POST request!") }) ``` Beyond the native `func(fiber.Ctx)` forms, Fiber also adapts Express-style, `net/http`, and `fasthttp` handlers. See [Handler types](../guide/routing.md#handler-types) in the routing guide for the full list of supported shapes. ### Use `Use` mounts middleware on a **prefix** (or **mount**) path: it runs for every request whose path begins with that prefix, on any HTTP method. Prefixes require either an exact match or a slash boundary, so `/john` matches `/john` and `/john/doe` but not `/johnnnnn`. Parameter tokens like `:name`, `:name?`, `*`, and `+` are still expanded before the boundary check runs. Called without a path, `Use` matches every request. ```go title="Signature" func (app *App) Use(args ...any) Router // Fiber inspects args to support these common usage patterns: // - app.Use(handler, handlers ...any) // - app.Use(path string, handler, handlers ...any) // - app.Use(paths []string, handler, handlers ...any) // - app.Use(path string, subApp *App) ``` Each handler argument can independently be a Fiber handler (with or without an `error` return), an Express-style callback, a `net/http` handler, or any other supported shape including fasthttp callbacks that return errors. ```go title="Examples" // Match any request app.Use(func(c fiber.Ctx) error { return c.Next() }) // Match request starting with /api app.Use("/api", func(c fiber.Ctx) error { return c.Next() }) // Match requests starting with /api or /home (multiple-prefix support) app.Use([]string{"/api", "/home"}, func(c fiber.Ctx) error { return c.Next() }) // Attach multiple handlers (they run in order; each must call c.Next() to continue) app.Use("/api", func(c fiber.Ctx) error { c.Set("X-Custom-Header", "value") return c.Next() }, func(c fiber.Ctx) error { return c.Next() }) // Mount a sub-app app.Use("/api", api) ``` ### Mounting Mount another Fiber instance with [`app.Use`](#use), similar to Express's [`router.use`](https://expressjs.com/en/api.html#router.use). ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() micro := fiber.New() // Mount the micro app on the "/john" route app.Use("/john", micro) // GET /john/doe -> 200 OK micro.Get("/doe", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }) log.Fatal(app.Listen(":3000")) } ``` :::caution Unlike Express, Fiber does not strip the mount prefix. Inside the mounted app, `c.Path()` still returns the full request path (`/john/doe`, not `/doe`); there is no `req.baseUrl` equivalent. ::: ### MountPath The `MountPath` property contains one or more path patterns on which a sub-app was mounted. ```go title="Signature" func (app *App) MountPath() string ``` ```go title="Example" package main import ( "fmt" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() one := fiber.New() two := fiber.New() three := fiber.New() two.Use("/three", three) one.Use("/two", two) app.Use("/one", one) fmt.Println("Mount paths:") fmt.Println("one.MountPath():", one.MountPath()) // "/one" fmt.Println("two.MountPath():", two.MountPath()) // "/one/two" fmt.Println("three.MountPath():", three.MountPath()) // "/one/two/three" fmt.Println("app.MountPath():", app.MountPath()) // "" } ``` :::caution Mounting order is important for `MountPath`. To get mount paths properly, you should start mounting from the deepest app. ::: ### Group You can group routes by creating a `*Group` struct. ```go title="Signature" func (app *App) Group(prefix string, handlers ...any) Router ``` ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() api := app.Group("/api", handler) // /api v1 := api.Group("/v1", handler) // /api/v1 v1.Get("/list", handler) // /api/v1/list v1.Get("/user", handler) // /api/v1/user v2 := api.Group("/v2", handler) // /api/v2 v2.Get("/list", handler) // /api/v2/list v2.Get("/user", handler) // /api/v2/user log.Fatal(app.Listen(":3000")) } func handler(c fiber.Ctx) error { return c.SendString("Handler response") } ``` ### RouteChain Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. Similar to [`Express`](https://expressjs.com/en/api.html#app.route). ```go title="Signature" func (app *App) RouteChain(path string) Register ```
Click here to see the `Register` interface ```go type Register interface { All(handler any, handlers ...any) Register Get(handler any, handlers ...any) Register Head(handler any, handlers ...any) Register Post(handler any, handlers ...any) Register Put(handler any, handlers ...any) Register Delete(handler any, handlers ...any) Register Connect(handler any, handlers ...any) Register Options(handler any, handlers ...any) Register Trace(handler any, handlers ...any) Register Patch(handler any, handlers ...any) Register Query(handler any, handlers ...any) Register Add(methods []string, handler any, handlers ...any) Register RouteChain(path string) Register } ```
```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Use `RouteChain` as a chainable route declaration method app.RouteChain("/test").Get(func(c fiber.Ctx) error { return c.SendString("GET /test") }) app.RouteChain("/events").All(func(c fiber.Ctx) error { // Runs for all HTTP verbs first // Think of it as route-specific middleware! return c.Next() }). Get(func(c fiber.Ctx) error { return c.SendString("GET /events") }). Post(func(c fiber.Ctx) error { // Maybe add a new event... return c.SendString("POST /events") }) // Combine multiple routes app.RouteChain("/reports").RouteChain("/daily").Get(func(c fiber.Ctx) error { return c.SendString("GET /reports/daily") }) // Use multiple methods app.RouteChain("/api").Get(func(c fiber.Ctx) error { return c.SendString("GET /api") }).Post(func(c fiber.Ctx) error { return c.SendString("POST /api") }) log.Fatal(app.Listen(":3000")) } ``` ### Route Defines routes with a common prefix inside the supplied function. Internally it uses [`Group`](#group) to create a sub-router and accepts an optional name prefix. ```go title="Signature" func (app *App) Route(prefix string, fn func(router Router), name ...string) Router ``` ```go title="Example" app.Route("/test", func(api fiber.Router) { api.Get("/foo", handler).Name("foo") // /test/foo (name: test.foo) api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) }, "test.") ``` ### Domain Creates a router scoped to a specific hostname pattern. Routes registered through the returned `Router` only match requests whose hostname (from `c.Hostname()`) matches the pattern. Domain names are matched case-insensitively per [RFC 4343](https://www.rfc-editor.org/rfc/rfc4343). When `TrustProxy` is enabled and the proxy is trusted, the hostname may be derived from the `X-Forwarded-Host` header instead of the `Host` header. To prevent header spoofing, you must both enable `TrustProxy` and configure [`TrustProxyConfig`](https://docs.gofiber.io/api/fiber#trustproxyconfig) with the IPs or ranges of your trusted proxies. See the [TrustProxy documentation](https://docs.gofiber.io/api/fiber#trustproxy) for details. The pattern can contain parameters prefixed with `:`. Use [`DomainParam`](#domainparam) to retrieve them inside handlers. Domain routing has **zero performance impact** on routes that don't use it — the hostname check is applied as a handler wrapper, not a change to the core router. :::note Because domain filtering is applied at handler-execution time (not during route matching), Fiber's `405 Method Not Allowed` logic may advertise methods from domain-scoped routes even when the requesting host does not match the domain pattern. This is a known trade-off of the handler-wrapping approach — it avoids core router changes while keeping non-domain routes unaffected. ::: :::note When mounting sub-applications via `Domain(...).Use(*fiber.App)`, routes are cloned from the sub-app at mount time. This means the same sub-app can safely be mounted on multiple domains without double-wrapping, but routes registered on the sub-app **after** mounting will not inherit domain filtering. Register all sub-app routes before mounting. ::: ```go title="Signature" func (app *App) Domain(host string) Router ``` ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Static domain — only matches requests to api.example.com app.Domain("api.example.com").Get("/users", func(c fiber.Ctx) error { return c.SendString("API users list") }) // Domain with parameter app.Domain(":user.blog.example.com").Get("/", func(c fiber.Ctx) error { user := fiber.DomainParam(c, "user") return c.SendString(user + "'s blog") }) // Composable with groups and middleware admin := app.Domain("admin.example.com") admin.Use(func(c fiber.Ctx) error { // Only runs for admin.example.com c.Set("X-Admin", "true") return c.Next() }) admin.Get("/dashboard", func(c fiber.Ctx) error { return c.SendString("Admin Dashboard") }) // Mount sub-applications on domain routers subApp := fiber.New() subApp.Get("/users", func(c fiber.Ctx) error { return c.SendString("Users list") }) app.Domain("api.example.com").Use("/api", subApp) // Fallback for unmatched domains app.Get("/", func(c fiber.Ctx) error { return c.SendString("Default site") }) log.Fatal(app.Listen(":3000")) } ``` #### DomainParam Returns the value of a domain parameter captured by a [`Domain`](#domain) pattern. If the key is not found, the optional default value is returned. ```go title="Signature" func DomainParam(c Ctx, key string, defaultValue ...string) string ``` ```go title="Example" // Pattern: ":tenant.example.com" // Request Host: acme.example.com app.Domain(":tenant.example.com").Get("/", func(c fiber.Ctx) error { tenant := fiber.DomainParam(c, "tenant") // "acme" missing := fiber.DomainParam(c, "missing", "none") // "none" return c.SendString(tenant + " " + missing) }) ``` ### HandlersCount Returns the number of registered handlers. ```go title="Signature" func (app *App) HandlersCount() uint32 ``` ### Stack Returns the underlying router stack. ```go title="Signature" func (app *App) Stack() [][]*Route ``` ```go title="Example" package main import ( "encoding/json" "fmt" "log" "github.com/gofiber/fiber/v3" ) var handler = func(c fiber.Ctx) error { return nil } func main() { app := fiber.New() app.Get("/john/:age", handler) app.Post("/register", handler) data, _ := json.MarshalIndent(app.Stack(), "", " ") fmt.Println(string(data)) log.Fatal(app.Listen(":3000")) } ```
Click here to see the result ```json [ [ { "method": "GET", "path": "/john/:age", "params": [ "age" ] } ], [ { "method": "HEAD", "path": "/john/:age", "params": [ "age" ] } ], [ { "method": "POST", "path": "/register", "params": null } ] ] ```
### Name This method assigns the name to the latest created route. ```go title="Signature" func (app *App) Name(name string) Router ``` ```go title="Example" package main import ( "encoding/json" "fmt" "log" "github.com/gofiber/fiber/v3" ) func main() { var handler = func(c fiber.Ctx) error { return nil } app := fiber.New() app.Get("/", handler) app.Name("index") app.Get("/doe", handler).Name("home") app.Trace("/tracer", handler).Name("tracert") app.Delete("/delete", handler).Name("delete") a := app.Group("/a") a.Name("fd.") a.Get("/test", handler).Name("test") data, _ := json.MarshalIndent(app.Stack(), "", " ") fmt.Println(string(data)) log.Fatal(app.Listen(":3000")) } ```
Click here to see the result ```json [ [ { "method": "GET", "name": "index", "path": "/", "params": null }, { "method": "GET", "name": "home", "path": "/doe", "params": null }, { "method": "GET", "name": "fd.test", "path": "/a/test", "params": null } ], [ { "method": "HEAD", "name": "", "path": "/", "params": null }, { "method": "HEAD", "name": "", "path": "/doe", "params": null }, { "method": "HEAD", "name": "", "path": "/a/test", "params": null } ], null, null, [ { "method": "DELETE", "name": "delete", "path": "/delete", "params": null } ], null, null, [ { "method": "TRACE", "name": "tracert", "path": "/tracer", "params": null } ], null ] ```
### GetRoute This method retrieves a route by its name. The returned `Route` can be inspected or used to generate a URL directly with `route.URL(params)`. ```go title="Signature" func (app *App) GetRoute(name string) Route ``` ```go title="Example" package main import ( "encoding/json" "fmt" "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", handler).Name("index") app.Get("/user/:name/:id", handler).Name("user") route := app.GetRoute("index") data, _ := json.MarshalIndent(route, "", " ") fmt.Println(string(data)) userRoute := app.GetRoute("user") location, _ := userRoute.URL(fiber.Map{"name": "john", "id": 1}) fmt.Println(location) // /user/john/1 log.Fatal(app.Listen(":3000")) } ```
Click here to see the result ```json { "method": "GET", "name": "index", "path": "/", "params": null } ```
### GetRoutes This method retrieves all routes. ```go title="Signature" func (app *App) GetRoutes(filterUseOption ...bool) []Route ``` When `filterUseOption` is set to `true`, it filters out routes registered by middleware. ```go title="Example" package main import ( "encoding/json" "fmt" "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Post("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }).Name("index") routes := app.GetRoutes(true) data, _ := json.MarshalIndent(routes, "", " ") fmt.Println(string(data)) log.Fatal(app.Listen(":3000")) } ```
Click here to see the result ```json [ { "method": "POST", "name": "index", "path": "/", "params": null } ] ```
## Config `Config` returns the [app config](./fiber.md#config) as a value (read-only). ```go title="Signature" func (app *App) Config() Config ``` ## Handler `Handler` returns the server handler that can be used to serve custom [`*fasthttp.RequestCtx`](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) requests. ```go title="Signature" func (app *App) Handler() fasthttp.RequestHandler ``` ## ErrorHandler `ErrorHandler` executes the process defined for the application in case of errors. This is used in some cases in middlewares. ```go title="Signature" func (app *App) ErrorHandler(ctx Ctx, err error) error ``` ## NewWithCustomCtx `NewWithCustomCtx` creates a new `*App` and sets the custom context factory function at construction time. ```go title="Signature" func NewWithCustomCtx(fn func(app *App) CustomCtx, config ...Config) *App ``` ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) type CustomCtx struct { fiber.DefaultCtx } func (c *CustomCtx) Params(key string, defaultValue ...string) string { return "prefix_" + c.DefaultCtx.Params(key) } func main() { app := fiber.NewWithCustomCtx(func(app *fiber.App) fiber.CustomCtx { return &CustomCtx{ DefaultCtx: *fiber.NewDefaultCtx(app), } }) app.Get("/:id", func(c fiber.Ctx) error { return c.SendString(c.Params("id")) }) log.Fatal(app.Listen(":3000")) } ``` ## RegisterCustomBinder You can register custom binders to use with [`Bind().Custom("name")`](bind.md#custom). They should be compatible with the `CustomBinder` interface. ```go title="Signature" func (app *App) RegisterCustomBinder(binder CustomBinder) ``` ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" "gopkg.in/yaml.v2" ) type User struct { Name string `yaml:"name"` } type customBinder struct{} func (*customBinder) Name() string { return "custom" } func (*customBinder) MIMETypes() []string { return []string{"application/yaml"} } func (*customBinder) Parse(c fiber.Ctx, out any) error { // Parse YAML body return yaml.Unmarshal(c.Body(), out) } func main() { app := fiber.New() // Register custom binder app.RegisterCustomBinder(&customBinder{}) app.Post("/custom", func(c fiber.Ctx) error { var user User // Use Custom binder by name if err := c.Bind().Custom("custom", &user); err != nil { return err } return c.JSON(user) }) app.Post("/normal", func(c fiber.Ctx) error { var user User // Custom binder is used by the MIME type if err := c.Bind().Body(&user); err != nil { return err } return c.JSON(user) }) log.Fatal(app.Listen(":3000")) } ``` ## RegisterCustomConstraint `RegisterCustomConstraint` allows you to register custom constraints. ```go title="Signature" func (app *App) RegisterCustomConstraint(constraint CustomConstraint) ``` See the [Custom Constraint](../guide/routing.md#custom-constraint) section for more information. ## SetTLSHandler Use `SetTLSHandler` to set [`ClientHelloInfo`](https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2) when using TLS with a `Listener`. ```go title="Signature" func (app *App) SetTLSHandler(tlsHandler *TLSHandler) ``` ## State / SharedState `State()` returns in-process state (local to the current process). `SharedState()` returns storage-backed state intended for prefork/multi-process sharing. ```go title="Signature" func (app *App) State() *State func (app *App) SharedState() *SharedState ``` See [State Management](./state.md) for usage and examples. ## Test Testing your application is done with the `Test` method. Use this method for creating `_test.go` files or when you need to debug your routing logic. The default timeout is `1s`; to disable a timeout altogether, pass a `TestConfig` struct with `Timeout: 0`. ```go title="Signature" func (app *App) Test(req *http.Request, config ...TestConfig) (*http.Response, error) ``` ```go title="Example" package main import ( "fmt" "io" "log" "net/http" "net/http/httptest" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Create route with GET method for test: app.Get("/", func(c fiber.Ctx) error { fmt.Println(c.BaseURL()) // => http://google.com fmt.Println(c.Get("X-Custom-Header")) // => hi return c.SendString("hello, World!") }) // Create http.Request req := httptest.NewRequest("GET", "http://google.com", nil) req.Header.Set("X-Custom-Header", "hi") // Perform the test resp, _ := app.Test(req) // Do something with the results: if resp.StatusCode == fiber.StatusOK { body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) // => hello, World! } } ``` If not provided, TestConfig is set to the following defaults: ```go title="Default TestConfig" config := fiber.TestConfig{ Timeout: time.Second, FailOnTimeout: true, } ``` :::caution Calling `app.Test(req)` uses the defaults above. Supplying an empty `fiber.TestConfig{}` instead is **not** equivalent; it is the same as supplying: ```go title="Empty TestConfig" cfg := fiber.TestConfig{ Timeout: 0, FailOnTimeout: false, } ``` This would make a Test that has no timeout. ::: ## Hooks `Hooks` is a method to return the [hooks](./hooks.md) property. ```go title="Signature" func (app *App) Hooks() *Hooks ``` ## Route Management Routes are normally defined before the app starts. You can also add or remove them at runtime with the methods below, but these operations are **not thread-safe** and are performance-intensive, so use them sparingly and only in development. ### RebuildTree The `RebuildTree` method is designed to rebuild the route tree and enable dynamic route registration. It returns a pointer to the `App` instance. ```go title="Signature" func (app *App) RebuildTree() *App ``` **Note:** Use this method with caution. It is **not** thread-safe and calling it can be very performance-intensive, so it should be used sparingly and only in development mode. Avoid using it concurrently. Here’s an example of how to define and register routes dynamically: ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/define", func(c fiber.Ctx) error { // Define a new route dynamically app.Get("/dynamically-defined", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }) // Rebuild the route tree to register the new route app.RebuildTree() return c.SendStatus(fiber.StatusOK) }) log.Fatal(app.Listen(":3000")) } ``` In this example, a new route is defined and then `RebuildTree()` is called to ensure the new route is registered and available. ### RemoveRoute This method removes a route by path. You must call the `RebuildTree()` method after the removal to finalize the update and rebuild the routing tree. If no methods are specified, the route will be removed for all HTTP methods defined in the app. To limit removal to specific methods, provide them as additional arguments. ```go title="Signature" func (app *App) RemoveRoute(path string, methods ...string) ``` ```go title="Example" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/api/feature-a", func(c fiber.Ctx) error { app.RemoveRoute("/api/feature", fiber.MethodGet) app.RebuildTree() // Redefine route app.Get("/api/feature", func(c fiber.Ctx) error { return c.SendString("Testing feature-a") }) app.RebuildTree() return c.SendStatus(fiber.StatusOK) }) app.Get("/api/feature-b", func(c fiber.Ctx) error { app.RemoveRoute("/api/feature", fiber.MethodGet) app.RebuildTree() // Redefine route app.Get("/api/feature", func(c fiber.Ctx) error { return c.SendString("Testing feature-b") }) app.RebuildTree() return c.SendStatus(fiber.StatusOK) }) log.Fatal(app.Listen(":3000")) } ``` ### RemoveRouteByName This method removes a route by name. If no methods are specified, the route will be removed for all HTTP methods defined in the app. To limit removal to specific methods, provide them as additional arguments. ```go title="Signature" func (app *App) RemoveRouteByName(name string, methods ...string) ``` ### RemoveRouteFunc This method removes a route by function having `*Route` parameter. If no methods are specified, the route will be removed for all HTTP methods defined in the app. To limit removal to specific methods, provide them as additional arguments. ```go title="Signature" func (app *App) RemoveRouteFunc(matchFunc func(r *Route) bool, methods ...string) ``` ## Helpers ### GetString Returns `s` unchanged when [`Immutable`](./fiber.md#immutable) is disabled or `s` resides in read-only memory. Otherwise, it returns a detached copy using `strings.Clone`. ```go title="Signature" func (app *App) GetString(s string) string ``` ### GetBytes Returns `b` unchanged when [`Immutable`](./fiber.md#immutable) is disabled or `b` resides in read-only memory. Otherwise, it returns a detached copy. ```go title="Signature" func (app *App) GetBytes(b []byte) []byte ``` ### ReloadViews Reloads the configured view engine on demand by calling its `Load` method. Use this helper in development workflows (e.g., file watchers or debug-only routes) to pick up template changes without restarting the server. Returns an error if no view engine is configured or reloading fails. ```go title="Signature" func (app *App) ReloadViews() error ``` ```go title="Example" app := fiber.New(fiber.Config{Views: engine}) app.Get("/dev/reload", func(c fiber.Ctx) error { if err := app.ReloadViews(); err != nil { return err } return c.SendString("Templates reloaded") }) ``` --- ## 📎 Bind Bindings parse request and response bodies, query parameters, cookies, and more into structs. :::info Binder-returned values are valid only within the handler. To keep them, copy the data or enable the [**`Immutable`**](./ctx.md) setting. [Read more...](../#zero-allocation) ::: ## Binders - [All](#all) - [Body](#body) - [CBOR](#cbor) - [Form](#form) - [JSON](#json) - [MsgPack](#msgpack) - [XML](#xml) - [Cookie](#cookie) - [Header](#header) - [Query](#query) - [RespHeader](#respheader) - [URI](#uri) ### All The `All` function binds data from URL parameters, the request body, query parameters, headers, and cookies into `out`. Sources are applied in the following order using struct field tags. #### Precedence Order The binding sources have the following precedence: 1. **URL Parameters (URI)** 2. **Request Body (e.g., JSON or form data)** 3. **Query Parameters** 4. **Request Headers** 5. **Cookies** :::info The request body is only included as a binding source when the request has both a non-empty body **and** a non-empty `Content-Type` header. ::: ```go title="Signature" func (b *Bind) All(out any) error ``` ```go title="Example" type User struct { Name string `query:"name" json:"name" form:"name"` Email string `json:"email" form:"email"` Role string `header:"X-User-Role"` SessionID string `json:"session_id" cookie:"session_id"` ID int `uri:"id" query:"id" json:"id" form:"id"` } app.Post("/users", func(c fiber.Ctx) error { user := new(User) if err := c.Bind().All(user); err != nil { return err } // All available data is now bound to the user struct return c.JSON(user) }) ``` #### Custom Precedence By default, the `All` method binds data in the following precedence order: `URI params -> Body -> Query -> Headers -> Cookies`. If you need to override this behavior, you can define a custom precedence order for your struct using the `binding_source` tag. ```go title="Example" type CustomPrecedenceReq struct { // Specify the priority for binding using the binding_source tag. // In this example, query parameters take highest priority, followed by headers. // The tag can be placed on any top-level field in the struct. Name string `binding_source:"query,header,cookie,body,uri" query:"name" header:"x-name" json:"name"` } app.Post("/users", func(c fiber.Ctx) error { req := new(CustomPrecedenceReq) if err := c.Bind().All(req); err != nil { return err } // req.Name will take the value from the query parameter if it exists, otherwise header, etc. return c.JSON(req) }) ``` :::info For maximum performance, Fiber caches the precedence resolution per `reflect.Type` to reduce repeated parsing overhead. Additionally, the `binding_source` tag must be placed on a **top-level** field of the struct (tags inside embedded structs are ignored), and a struct may only contain **one** `binding_source` tag. - **Partial Lists**: If you omit sources from the tag (e.g., `binding_source:"query"`), the omitted sources (body, header, etc.) will **not** be bound at all. - **Unrecognized Sources**: If an invalid source name is provided in the tag (e.g., `binding_source:"invalid"`), Fiber will return an error during binding. ::: ### Body Binds the request body to a struct. Use tags that match the content type. For example, to parse a JSON body with a `Pass` field, declare `json:"pass"`. | Content-Type | Struct Tag | | ----------------------------------- | ---------- | | `application/x-www-form-urlencoded` | `form` | | `multipart/form-data` | `form` | | `application/json` | `json` | | `application/xml` | `xml` | | `text/xml` | `xml` | | `application/vnd.msgpack` | `msgpack` | ```go title="Signature" func (b *Bind) Body(out any) error ``` ```go title="Example" type Person struct { Name string `json:"name" xml:"name" form:"name" msgpack:"name"` Pass string `json:"pass" xml:"pass" form:"pass" msgpack:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Body(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Test the handler with these `curl` commands: ```bash # JSON curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000 # MsgPack curl -X POST -H "Content-Type: application/vnd.msgpack" --data-binary $'\x82\xa4name\xa4john\xa4pass\xa3doe' localhost:3000 # XML curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000 # Form URL-Encoded curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000 # Multipart Form curl -X POST -F name=john -F pass=doe http://localhost:3000 ``` ### CBOR > **Note:** Before using any CBOR-related features, make sure to follow the [CBOR setup instructions](../guide/advance-format.md#cbor). Binds the request CBOR body to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a CBOR body with a field called `Pass`, you would use a struct field with `cbor:"pass"`. ```go title="Signature" func (b *Bind) CBOR(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { Name string `cbor:"name"` Pass string `cbor:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().CBOR(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Test the defaults with this `curl` command: ```bash curl -X POST -H "Content-Type: application/cbor" --data "\xa2dnamedjohndpasscdoe" localhost:3000 ``` ### Form Binds the request or multipart form body data to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a form body with a field called `Pass`, you would use a struct field with `form:"pass"`. ```go title="Signature" func (b *Bind) Form(out any) error ``` ```go title="Example" type Person struct { Name string `form:"name"` Pass string `form:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Form(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Run tests with the following `curl` commands for both `application/x-www-form-urlencoded` and `multipart/form-data`: ```bash curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000 ``` ```bash curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000 ``` :::info If you need to bind multipart file, you can use `*multipart.FileHeader`, `*[]*multipart.FileHeader` or `[]*multipart.FileHeader` as a field type. ::: ```go title="Example" type Person struct { Name string `form:"name"` Pass string `form:"pass"` Avatar *multipart.FileHeader `form:"avatar"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Form(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe log.Println(p.Avatar.Filename) // file.txt // ... }) ``` Run tests with the following `curl` command: ```bash curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" -F 'avatar=@filename' localhost:3000 ``` ### JSON Binds the request JSON body to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called `Pass`, you would use a struct field with `json:"pass"`. ```go title="Signature" func (b *Bind) JSON(out any) error ``` ```go title="Example" type Person struct { Name string `json:"name"` Pass string `json:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().JSON(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Run tests with the following `curl` command: ```bash curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000 ``` ### MsgPack > **Note:** Before using any MsgPack-related features, make sure to follow the [MsgPack setup instructions](../guide/advance-format.md#msgpack). Binds the request MsgPack body to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a Msgpack body with a field called `Pass`, you would use a struct field with `msgpack:"pass"`. > Our library uses [shamaton-msgpack](https://github.com/shamaton/msgpack) which uses `msgpack` struct tags by default. If you want to use other libraries, you may need to update the struct tags accordingly. ```go title="Signature" func (b *Bind) MsgPack(out any) error ``` ```go title="Example" type Person struct { Name string `msgpack:"name"` Pass string `msgpack:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().MsgPack(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Run tests with the following `curl` command: ```bash curl -X POST -H "Content-Type: application/vnd.msgpack" --data-binary $'\x82\xa4name\xa4john\xa4pass\xa3doe' localhost:3000 ``` ### XML Binds the request XML body to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse an XML body with a field called `Pass`, you would use a struct field with `xml:"pass"`. ```go title="Signature" func (b *Bind) XML(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { Name string `xml:"name"` Pass string `xml:"pass"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().XML(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // ... }) ``` Run tests with the following `curl` command: ```bash curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000 ``` ### Cookie This method is similar to [Body Binding](#body), but for cookie parameters. It is important to use the struct tag `cookie`. For example, if you want to parse a cookie with a field called `Age`, you would use a struct field with `cookie:"age"`. ```go title="Signature" func (b *Bind) Cookie(out any) error ``` ```go title="Example" type Person struct { Name string `cookie:"name"` Age int `cookie:"age"` Job bool `cookie:"job"` } app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Cookie(p); err != nil { return err } log.Println(p.Name) // Joseph log.Println(p.Age) // 23 log.Println(p.Job) // true }) ``` Run tests with the following `curl` command: ```bash curl --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ ``` ### Header This method is similar to [Body Binding](#body), but for request headers. It is important to use the struct tag `header`. For example, if you want to parse a request header with a field called `Pass`, you would use a struct field with `header:"pass"`. ```go title="Signature" func (b *Bind) Header(out any) error ``` ```go title="Example" type Person struct { Name string `header:"name"` Pass string `header:"pass"` Products []string `header:"products"` } app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Header(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe log.Println(p.Products) // [shoe hat] // ... }) ``` Run tests with the following `curl` command: ```bash curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" ``` ### Query This method is similar to [Body Binding](#body), but for query parameters. It is important to use the struct tag `query`. For example, if you want to parse a query parameter with a field called `Pass`, you would use a struct field with `query:"pass"`. ```go title="Signature" func (b *Bind) Query(out any) error ``` ```go title="Example" type Person struct { Name string `query:"name"` Pass string `query:"pass"` Products []string `query:"products"` } app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Query(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe // Depending on fiber.Config{EnableSplittingOnParsers: false} - default log.Println(p.Products) // ["shoe,hat"] // With fiber.Config{EnableSplittingOnParsers: true} // log.Println(p.Products) // ["shoe", "hat"] // ... }) ``` Run tests with the following `curl` command: ```bash curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat" ``` :::info For more parser settings, please refer to [Config](fiber.md#enablesplittingonparsers) ::: #### Array Query Parameters Fiber supports several formats for passing array values via query parameters. The following table gives an overview: | Format | Example | Requires `EnableSplittingOnParsers` | | ------------------------ | ---------------------------------------------- | ----------------------------------- | | Repeated key | `?colors=red&colors=blue` | No | | Bracket notation | `?colors[]=red&colors[]=blue` | No | | Comma-separated | `?colors=red,blue` | **Yes** | | Indexed bracket notation | `?posts[0][title]=Hello&posts[1][title]=World` | No | | Nested bracket notation | `?preferences[tags]=golang,api` | No (comma splitting: **Yes**) | ##### Repeated Key The most common approach. Repeat the same query key for each value: ```text GET /api?colors=red&colors=blue&colors=green ``` ```go title="Struct" type Filter struct { Colors []string `query:"colors"` } // Result: Colors = ["red", "blue", "green"] ``` ```bash title="curl" curl "http://localhost:3000/api?colors=red&colors=blue&colors=green" ``` ##### Bracket Notation Append `[]` to the key name. This is common in PHP-style and JavaScript frameworks: ```text GET /api?colors[]=red&colors[]=blue&colors[]=green ``` ```go title="Struct" type Filter struct { Colors []string `query:"colors"` } // Result: Colors = ["red", "blue", "green"] ``` ```bash title="curl" curl "http://localhost:3000/api?colors[]=red&colors[]=blue&colors[]=green" ``` :::note The struct field tag stays `query:"colors"` (without brackets). Fiber strips the `[]` automatically. ::: ##### Comma-Separated Values Pass multiple values in a single parameter, separated by commas. This format requires [`EnableSplittingOnParsers`](fiber.md#enablesplittingonparsers) to be set to `true`. ```text GET /api?colors=red,blue,green ``` ```go title="Struct" type Filter struct { Colors []string `query:"colors"` } ``` ```go title="App Setup" // EnableSplittingOnParsers is required for comma splitting app := fiber.New(fiber.Config{ EnableSplittingOnParsers: true, }) // Result: Colors = ["red", "blue", "green"] ``` Without `EnableSplittingOnParsers`, the entire string `"red,blue,green"` is treated as a **single** element. ```go title="Default behavior (EnableSplittingOnParsers: false)" // GET /api?colors=red,blue,green // Result: Colors = ["red,blue,green"] ← single element ``` ```bash title="curl" curl "http://localhost:3000/api?colors=red,blue,green" ``` You can also mix comma-separated values with repeated keys when splitting is enabled: ```text GET /api?hobby=soccer&hobby=basketball,football ``` ```go type Query struct { Hobby []string `query:"hobby"` } // With EnableSplittingOnParsers: true // Result: Hobby = ["soccer", "basketball", "football"] ← 3 elements ``` ##### Indexed Bracket Notation (Nested Structs) Use indexed brackets to bind arrays of nested structs: ```text GET /api?posts[0][title]=Hello&posts[0][author]=Alice&posts[1][title]=World&posts[1][author]=Bob ``` ```go title="Struct" type Post struct { Title string `query:"title"` Author string `query:"author"` } type Request struct { Posts []Post `query:"posts"` } // Result: Posts = [{Title: "Hello", Author: "Alice"}, {Title: "World", Author: "Bob"}] ``` ```bash title="curl" curl "http://localhost:3000/api?posts[0][title]=Hello&posts[0][author]=Alice&posts[1][title]=World&posts[1][author]=Bob" ``` ##### Nested Bracket Notation (Without Index) Use bracket notation to access fields of a nested struct: ```text GET /api?preferences[tags]=golang,api ``` ```go title="Struct" type Preferences struct { Tags *[]string `query:"tags"` } type Profile struct { Prefs *Preferences `query:"preferences"` } // With EnableSplittingOnParsers: true // Result: *Prefs.Tags = ["golang", "api"] ``` ```bash title="curl" curl "http://localhost:3000/api?preferences[tags]=golang,api" ``` :::note Pointer fields (`*[]string`, `*Preferences`) let you distinguish between a missing parameter (`nil`) and an empty one. When the parameter is present, Fiber allocates the pointer automatically. ::: ### RespHeader This method is similar to [Body Binding](#body), but for response headers. It is important to use the struct tag `respHeader`. For example, if you want to parse a response header with a field called `Pass`, you would use a struct field with `respHeader:"pass"`. ```go title="Signature" func (b *Bind) RespHeader(out any) error ``` ```go title="Example" type Person struct { Name string `respHeader:"name"` Pass string `respHeader:"pass"` Products []string `respHeader:"products"` } app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().RespHeader(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe log.Println(p.Products) // [shoe hat] // ... }) ``` Run tests with the following `curl` command: ```bash curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" ``` ### URI This method is similar to [Body Binding](#body), but for path parameters. It is important to use the struct tag `uri`. For example, if you want to parse a path parameter with a field called `Pass`, you would use a struct field with `uri:"pass"`. ```go title="Signature" func (b *Bind) URI(out any) error ``` ```go title="Example" // GET http://example.com/user/111 app.Get("/user/:id", func(c fiber.Ctx) error { param := struct { ID uint `uri:"id"` }{} if err := c.Bind().URI(¶m); err != nil { return err } // ... return c.SendString(fmt.Sprintf("User ID: %d", param.ID)) }) ``` ## BindError When a bind method fails to parse (e.g. invalid JSON, bad type conversion), the behavior depends on the error-handling mode. In **manual handling** (the default), the binder returns a `*BindError` wrapping the underlying error — use `errors.As` to extract it and branch on the binding source or field. In **automatic handling** (enabled via `WithAutoHandling`), parse failures are instead converted to a `*fiber.Error` with HTTP status 400; `*BindError` is never surfaced to the caller in that mode. If you are using `WithAutoHandling`, check for `*fiber.Error` or an HTTP 400 response rather than using `errors.As` for `*BindError`. ```go type BindError struct { Source string // "uri", "query", "body", "header", "cookie", or "respHeader" Field string // struct field or tag key that failed (best-effort, may be empty) Err error // underlying error; use errors.As to inspect } ``` Source constants: `BindSourceURI`, `BindSourceQuery`, `BindSourceHeader`, `BindSourceCookie`, `BindSourceBody`, `BindSourceRespHeader`. ### Branching on source Use `errors.As` to extract `*BindError` and branch on `Source` for RFC-correct status codes (e.g. 404 for URI failures vs 400 for body/query): ```go title="Example" // With manual handling mode (default behavior) // Will not work with WithAutoHandling() var req struct { ID int `uri:"id"` Name string `json:"name"` } if err := c.Bind().All(&req); err != nil { var be *fiber.BindError if errors.As(err, &be) && be.Source == fiber.BindSourceURI { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"}) } return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request"}) } ``` ### Validation vs binding errors Validation errors (from `StructValidator`) are **not** wrapped in `BindError`. Use `errors.As(err, &be)` to distinguish: it succeeds only for parsing/binding failures, not for validation failures. ## Custom To use custom binders, you have to use this method. You can register them using the [RegisterCustomBinder](./app.md#registercustombinder) method of the Fiber instance. ```go title="Signature" func (b *Bind) Custom(name string, dest any) error ``` ```go title="Example" app := fiber.New() // My custom binder type customBinder struct{} func (cb *customBinder) Name() string { return "custom" } func (cb *customBinder) MIMETypes() []string { return []string{"application/yaml"} } func (cb *customBinder) Parse(c fiber.Ctx, out any) error { // parse YAML body return yaml.Unmarshal(c.Body(), out) } // Register custom binder app.RegisterCustomBinder(&customBinder{}) type User struct { Name string `yaml:"name"` } // curl -X POST http://localhost:3000/custom -H "Content-Type: application/yaml" -d "name: John" app.Post("/custom", func(c fiber.Ctx) error { var user User // Use Custom binder by name if err := c.Bind().Custom("custom", &user); err != nil { return err } return c.JSON(user) }) ``` Internally, custom binders are also used in the [Body](#body) method. The `MIMETypes` method is used to check if the custom binder should be used for the given content type. ## Options For more control over error handling, you can use the following methods. ### WithAutoHandling If you want to handle binder errors automatically, you can use `WithAutoHandling`. If there's an error, it will return the error and set HTTP status to `400 Bad Request`. This function does NOT panic therefore you must still return on error explicitly ```go title="Signature" func (b *Bind) WithAutoHandling() *Bind ``` ### WithoutAutoHandling To handle binder errors manually, you can use the `WithoutAutoHandling` method. It's the default behavior of the binder. ```go title="Signature" func (b *Bind) WithoutAutoHandling() *Bind ``` ### SkipValidation To enable or disable validation for the current bind chain, use `SkipValidation`. By default, validation is enabled (`skip = false`). ```go title="Signature" func (b *Bind) SkipValidation(skip bool) *Bind ``` ## SetParserDecoder Allows you to configure the BodyParser/QueryParser decoder based on schema options, providing the possibility to add custom types for parsing. ```go title="Signature" func SetParserDecoder(parserConfig binder.ParserConfig) ``` `binder.ParserConfig` has the following fields: ```go type ParserConfig struct { IgnoreUnknownKeys bool ParserType []ParserType ZeroEmpty bool SetAliasTag string } type ParserType struct { CustomType any Converter func(string) reflect.Value } ``` ```go title="Example" type CustomTime time.Time // String returns the time in string format func (ct *CustomTime) String() string { t := time.Time(*ct).String() return t } // Converter for CustomTime type with format "2006-01-02" var timeConverter = func(value string) reflect.Value { fmt.Println("timeConverter:", value) if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(CustomTime(v)) } return reflect.Value{} } customTime := binder.ParserType{ CustomType: CustomTime{}, Converter: timeConverter, } // Add custom type to the Decoder settings binder.SetParserDecoder(binder.ParserConfig{ IgnoreUnknownKeys: true, ParserType: []binder.ParserType{customTime}, ZeroEmpty: true, }) // Example using CustomTime with non-RFC3339 format type Demo struct { Date CustomTime `form:"date" query:"date"` Title string `form:"title" query:"title"` Body string `form:"body" query:"body"` } app.Post("/body", func(c fiber.Ctx) error { var d Demo if err := c.Bind().Body(&d); err != nil { return err } fmt.Println("d.Date:", d.Date.String()) return c.JSON(d) }) app.Get("/query", func(c fiber.Ctx) error { var d Demo if err := c.Bind().Query(&d); err != nil { return err } fmt.Println("d.Date:", d.Date.String()) return c.JSON(d) }) // Run tests with the following curl commands: # Body Binding curl -X POST -F title=title -F body=body -F date=2021-10-20 http://localhost:3000/body # Query Binding curl -X GET "http://localhost:3000/query?title=title&body=body&date=2021-10-20" ``` ## Validation Validation is also possible with the binding methods. You can specify your validation rules using the `validate` struct tag. Specify your struct validator in the [config](./fiber.md#structvalidator). The validator must implement the `StructValidator` interface, which requires a `Validate` method that takes an `any` type and returns an error. ```go title="Interface" type StructValidator interface { Validate(out any) error } ``` ### Setup Your Validator in the Config ```go title="Example" import "github.com/go-playground/validator/v10" type structValidator struct { validate *validator.Validate } // Validate method implementation func (v *structValidator) Validate(out any) error { return v.validate.Struct(out) } // Setup your validator in the Fiber config app := fiber.New(fiber.Config{ StructValidator: &structValidator{validate: validator.New()}, }) ``` Fiber only runs `StructValidator` for struct destinations (or pointers to structs). Binding into maps and other non-struct types skips the validator step. ### Usage of Validation in Binding Methods ```go title="Example" type Person struct { Name string `json:"name" validate:"required"` Age int `json:"age" validate:"gte=18,lte=60"` } app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().JSON(p); err != nil { // Receives validation errors return err } }) ``` ## Default Fields You can set default values for fields in the struct by using the `default` struct tag. Supported types: - `bool` - Float variants (`float32`, `float64`) - Int variants (`int`, `int8`, `int16`, `int32`, `int64`) - Uint variants (`uint`, `uint8`, `uint16`, `uint32`, `uint64`) - `string` - A slice of the above types. Use `|` to separate slice items. - A pointer to one of the above types (**pointers to slices and slices of pointers are not supported**). ```go title="Example" type Person struct { Name string `query:"name,default:john"` Pass string `query:"pass"` Products []string `query:"products,default:shoe|hat"` } app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.Bind().Query(p); err != nil { return err } log.Println(p.Name) // john log.Println(p.Pass) // doe log.Println(p.Products) // ["shoe", "hat"] // ... }) ``` Run tests with the following `curl` command: ```bash curl "http://localhost:3000/?pass=doe" ``` --- ## 📋 Constants ### HTTP methods (mirrors `net/http`) ```go const ( MethodGet = "GET" // RFC 7231, 4.3.1 MethodHead = "HEAD" // RFC 7231, 4.3.2 MethodPost = "POST" // RFC 7231, 4.3.3 MethodPut = "PUT" // RFC 7231, 4.3.4 MethodPatch = "PATCH" // RFC 5789 MethodDelete = "DELETE" // RFC 7231, 4.3.5 MethodConnect = "CONNECT" // RFC 7231, 4.3.6 MethodOptions = "OPTIONS" // RFC 7231, 4.3.7 MethodTrace = "TRACE" // RFC 7231, 4.3.8 MethodQuery = "QUERY" // RFC 10008 methodUse = "USE" ) ``` ### Common MIME types ```go const ( MIMETextXML = "text/xml" MIMETextHTML = "text/html" MIMETextPlain = "text/plain" MIMETextJavaScript = "text/javascript" MIMETextCSS = "text/css" MIMETextEventStream = "text/event-stream" MIMEApplicationXML = "application/xml" MIMEApplicationJSON = "application/json" MIMEApplicationJavaScript = "application/javascript" MIMEApplicationCBOR = "application/cbor" MIMEApplicationForm = "application/x-www-form-urlencoded" MIMEOctetStream = "application/octet-stream" MIMEMultipartForm = "multipart/form-data" MIMEApplicationMsgPack = "application/vnd.msgpack" MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8" MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8" MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8" MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8" MIMETextCSSCharsetUTF8 = "text/css; charset=utf-8" MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8" MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8" ) ``` ### HTTP status codes (mirrors `net/http`) ```go const ( StatusContinue = 100 // RFC 9110, 15.2.1 StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2 StatusProcessing = 102 // RFC 2518, 10.1 StatusEarlyHints = 103 // RFC 8297 StatusOK = 200 // RFC 9110, 15.3.1 StatusCreated = 201 // RFC 9110, 15.3.2 StatusAccepted = 202 // RFC 9110, 15.3.3 StatusNonAuthoritativeInformation = 203 // RFC 9110, 15.3.4 StatusNoContent = 204 // RFC 9110, 15.3.5 StatusResetContent = 205 // RFC 9110, 15.3.6 StatusPartialContent = 206 // RFC 9110, 15.3.7 StatusMultiStatus = 207 // RFC 4918, 11.1 StatusAlreadyReported = 208 // RFC 5842, 7.1 StatusIMUsed = 226 // RFC 3229, 10.4.1 StatusMultipleChoices = 300 // RFC 9110, 15.4.1 StatusMovedPermanently = 301 // RFC 9110, 15.4.2 StatusFound = 302 // RFC 9110, 15.4.3 StatusSeeOther = 303 // RFC 9110, 15.4.4 StatusNotModified = 304 // RFC 9110, 15.4.5 StatusUseProxy = 305 // RFC 9110, 15.4.6 StatusSwitchProxy = 306 // RFC 9110, 15.4.7 (Unused) StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8 StatusPermanentRedirect = 308 // RFC 9110, 15.4.9 StatusBadRequest = 400 // RFC 9110, 15.5.1 StatusUnauthorized = 401 // RFC 9110, 15.5.2 StatusPaymentRequired = 402 // RFC 9110, 15.5.3 StatusForbidden = 403 // RFC 9110, 15.5.4 StatusNotFound = 404 // RFC 9110, 15.5.5 StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6 StatusNotAcceptable = 406 // RFC 9110, 15.5.7 StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8 StatusRequestTimeout = 408 // RFC 9110, 15.5.9 StatusConflict = 409 // RFC 9110, 15.5.10 StatusGone = 410 // RFC 9110, 15.5.11 StatusLengthRequired = 411 // RFC 9110, 15.5.12 StatusPreconditionFailed = 412 // RFC 9110, 15.5.13 StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14 StatusRequestURITooLong = 414 // RFC 9110, 15.5.15 StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16 StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 StatusExpectationFailed = 417 // RFC 9110, 15.5.18 StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused) StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20 StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21 StatusLocked = 423 // RFC 4918, 11.3 StatusFailedDependency = 424 // RFC 4918, 11.4 StatusTooEarly = 425 // RFC 8470, 5.2. StatusUpgradeRequired = 426 // RFC 9110, 15.5.22 StatusPreconditionRequired = 428 // RFC 6585, 3 StatusTooManyRequests = 429 // RFC 6585, 4 StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 StatusInternalServerError = 500 // RFC 9110, 15.6.1 StatusNotImplemented = 501 // RFC 9110, 15.6.2 StatusBadGateway = 502 // RFC 9110, 15.6.3 StatusServiceUnavailable = 503 // RFC 9110, 15.6.4 StatusGatewayTimeout = 504 // RFC 9110, 15.6.5 StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6 StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 StatusInsufficientStorage = 507 // RFC 4918, 11.5 StatusLoopDetected = 508 // RFC 5842, 7.2 StatusNotExtended = 510 // RFC 2774, 7 StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 ) ``` ### Errors ```go var ( ErrBadRequest = NewError(StatusBadRequest) // 400 ErrUnauthorized = NewError(StatusUnauthorized) // 401 ErrPaymentRequired = NewError(StatusPaymentRequired) // 402 ErrForbidden = NewError(StatusForbidden) // 403 ErrNotFound = NewError(StatusNotFound) // 404 ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // 405 ErrNotAcceptable = NewError(StatusNotAcceptable) // 406 ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // 407 ErrRequestTimeout = NewError(StatusRequestTimeout) // 408 ErrConflict = NewError(StatusConflict) // 409 ErrGone = NewError(StatusGone) // 410 ErrLengthRequired = NewError(StatusLengthRequired) // 411 ErrPreconditionFailed = NewError(StatusPreconditionFailed) // 412 ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // 413 ErrRequestURITooLong = NewError(StatusRequestURITooLong) // 414 ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // 415 ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // 416 ErrExpectationFailed = NewError(StatusExpectationFailed) // 417 ErrTeapot = NewError(StatusTeapot) // 418 ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // 421 ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // 422 ErrLocked = NewError(StatusLocked) // 423 ErrFailedDependency = NewError(StatusFailedDependency) // 424 ErrTooEarly = NewError(StatusTooEarly) // 425 ErrUpgradeRequired = NewError(StatusUpgradeRequired) // 426 ErrPreconditionRequired = NewError(StatusPreconditionRequired) // 428 ErrTooManyRequests = NewError(StatusTooManyRequests) // 429 ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // 431 ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // 451 ErrInternalServerError = NewError(StatusInternalServerError) // 500 ErrNotImplemented = NewError(StatusNotImplemented) // 501 ErrBadGateway = NewError(StatusBadGateway) // 502 ErrServiceUnavailable = NewError(StatusServiceUnavailable) // 503 ErrGatewayTimeout = NewError(StatusGatewayTimeout) // 504 ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // 505 ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // 506 ErrInsufficientStorage = NewError(StatusInsufficientStorage) // 507 ErrLoopDetected = NewError(StatusLoopDetected) // 508 ErrNotExtended = NewError(StatusNotExtended) // 510 ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // 511 ) ``` HTTP Headers were copied from net/http. ```go const ( HeaderAuthorization = "Authorization" HeaderProxyAuthenticate = "Proxy-Authenticate" HeaderProxyAuthorization = "Proxy-Authorization" HeaderWWWAuthenticate = "WWW-Authenticate" HeaderAge = "Age" HeaderCacheControl = "Cache-Control" HeaderClearSiteData = "Clear-Site-Data" HeaderExpires = "Expires" HeaderPragma = "Pragma" HeaderWarning = "Warning" HeaderAcceptCH = "Accept-CH" HeaderAcceptCHLifetime = "Accept-CH-Lifetime" HeaderContentDPR = "Content-DPR" HeaderDPR = "DPR" HeaderEarlyData = "Early-Data" HeaderSaveData = "Save-Data" HeaderViewportWidth = "Viewport-Width" HeaderWidth = "Width" HeaderETag = "ETag" HeaderIfMatch = "If-Match" HeaderIfModifiedSince = "If-Modified-Since" HeaderIfNoneMatch = "If-None-Match" HeaderIfUnmodifiedSince = "If-Unmodified-Since" HeaderLastModified = "Last-Modified" HeaderVary = "Vary" HeaderConnection = "Connection" HeaderKeepAlive = "Keep-Alive" HeaderAccept = "Accept" HeaderAcceptCharset = "Accept-Charset" HeaderAcceptEncoding = "Accept-Encoding" HeaderAcceptLanguage = "Accept-Language" HeaderCookie = "Cookie" HeaderExpect = "Expect" HeaderMaxForwards = "Max-Forwards" HeaderSetCookie = "Set-Cookie" HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers" HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods" HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin" HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers" HeaderAccessControlMaxAge = "Access-Control-Max-Age" HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers" HeaderAccessControlRequestMethod = "Access-Control-Request-Method" HeaderOrigin = "Origin" HeaderTimingAllowOrigin = "Timing-Allow-Origin" HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies" HeaderDNT = "DNT" HeaderTk = "Tk" HeaderContentDisposition = "Content-Disposition" HeaderContentEncoding = "Content-Encoding" HeaderContentLanguage = "Content-Language" HeaderContentLength = "Content-Length" HeaderContentLocation = "Content-Location" HeaderContentType = "Content-Type" HeaderForwarded = "Forwarded" HeaderVia = "Via" HeaderXForwardedFor = "X-Forwarded-For" HeaderXForwardedHost = "X-Forwarded-Host" HeaderXForwardedProto = "X-Forwarded-Proto" HeaderXForwardedProtocol = "X-Forwarded-Protocol" HeaderXForwardedSsl = "X-Forwarded-Ssl" HeaderXUrlScheme = "X-Url-Scheme" HeaderLocation = "Location" HeaderFrom = "From" HeaderHost = "Host" HeaderReferer = "Referer" HeaderReferrerPolicy = "Referrer-Policy" HeaderUserAgent = "User-Agent" HeaderAllow = "Allow" HeaderServer = "Server" HeaderAcceptRanges = "Accept-Ranges" HeaderContentRange = "Content-Range" HeaderIfRange = "If-Range" HeaderRange = "Range" HeaderContentSecurityPolicy = "Content-Security-Policy" HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only" HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy" HeaderExpectCT = "Expect-CT" HeaderPermissionsPolicy = "Permissions-Policy" HeaderPublicKeyPins = "Public-Key-Pins" HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only" HeaderStrictTransportSecurity = "Strict-Transport-Security" HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests" HeaderXContentTypeOptions = "X-Content-Type-Options" HeaderXDownloadOptions = "X-Download-Options" HeaderXFrameOptions = "X-Frame-Options" HeaderXPoweredBy = "X-Powered-By" HeaderXXSSProtection = "X-XSS-Protection" HeaderLastEventID = "Last-Event-ID" HeaderNEL = "NEL" HeaderPingFrom = "Ping-From" HeaderPingTo = "Ping-To" HeaderReportTo = "Report-To" HeaderTE = "TE" HeaderTrailer = "Trailer" HeaderTransferEncoding = "Transfer-Encoding" HeaderSecFetchSite = "Sec-Fetch-Site" HeaderSecWebSocketAccept = "Sec-WebSocket-Accept" HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions" HeaderSecWebSocketKey = "Sec-WebSocket-Key" HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol" HeaderSecWebSocketVersion = "Sec-WebSocket-Version" HeaderAcceptPatch = "Accept-Patch" HeaderAcceptPushPolicy = "Accept-Push-Policy" HeaderAcceptSignature = "Accept-Signature" HeaderAltSvc = "Alt-Svc" HeaderDate = "Date" HeaderIndex = "Index" HeaderLargeAllocation = "Large-Allocation" HeaderLink = "Link" HeaderPushPolicy = "Push-Policy" HeaderRetryAfter = "Retry-After" HeaderServerTiming = "Server-Timing" HeaderSignature = "Signature" HeaderSignedHeaders = "Signed-Headers" HeaderSourceMap = "SourceMap" HeaderUpgrade = "Upgrade" HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control" HeaderXPingback = "X-Pingback" HeaderXRequestID = "X-Request-ID" HeaderXRequestedWith = "X-Requested-With" HeaderXResponseTime = "X-Response-Time" HeaderXRobotsTag = "X-Robots-Tag" HeaderXUACompatible = "X-UA-Compatible" HeaderAccessControlAllowPrivateNetwork = "Access-Control-Allow-Private-Network" HeaderAccessControlRequestPrivateNetwork = "Access-Control-Request-Private-Network" ) ``` --- ## 🧠 Ctx Use the index to jump straight to any `Ctx` method; filter by name or by category: ### Abandon Marks the context as abandoned. An abandoned context will not be returned to the pool when `ReleaseCtx` is called. This is used internally by the [timeout middleware](../middleware/timeout.md) to return immediately while the handler goroutine continues safely. ```go title="Signature" func (c fiber.Ctx) Abandon() func (c fiber.Ctx) IsAbandoned() bool func (c fiber.Ctx) ForceRelease() ``` | Method | Description | |:---------------|:----------------------------------------------------------------------------| | `Abandon()` | Marks the context as abandoned. ReleaseCtx becomes a no-op for this context. | | `IsAbandoned()`| Returns `true` if `Abandon()` was called on this context. | | `ForceRelease()`| Releases an abandoned context back to the pool. Must only be called after the handler has completely finished. | :::caution These methods are primarily for internal use and advanced middleware development. Most applications should not need to call them directly. ::: ### App Returns the [\*App](app.md) reference so you can easily access all application settings. ```go title="Signature" func (c fiber.Ctx) App() *App ``` ```go title="Example" app.Get("/stack", func(c fiber.Ctx) error { return c.JSON(c.App().Stack()) }) ``` ### Bind Bind returns a helper for decoding the request body, query string, headers, cookies, and more. For full details, see the [Bind](./bind.md) documentation. ```go title="Signature" func (c fiber.Ctx) Bind() *Bind ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { user := new(User) // Bind the request body to a struct: return c.Bind().Body(user) }) ``` ### Context Returns a `context.Context` that was previously set with [`SetContext`](#setcontext). If no context was set, it returns `context.Background()`. Unlike `fiber.Ctx` itself, the returned context is safe to use after the handler completes. ```go title="Signature" func (c fiber.Ctx) Context() context.Context ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { ctx := c.Context() go doWork(ctx) return nil }) ``` ### context.Context `Ctx` implements `context.Context`, but as a context that can never be canceled: `Deadline()` reports no deadline, `Done()` returns `nil` and `Err()` returns `nil`, regardless of what you pass to [`SetContext`](#setcontext). The `fiber.Ctx` instance is pooled and reused after the handler returns, which is why it cannot carry cancellation of its own. Call [`Context`](#context) within the handler to obtain a real `context.Context`, and pass that to anything that is cancellation-aware or that outlives the handler. ```go title="Signature" func (c fiber.Ctx) Deadline() (deadline time.Time, ok bool) func (c fiber.Ctx) Done() <-chan struct{} func (c fiber.Ctx) Err() error func (c fiber.Ctx) Value(key any) any ``` ```go title="Example" func doSomething(ctx context.Context) { // ... } app.Get("/", func(c fiber.Ctx) error { doSomething(c) return nil }) ``` :::caution Passing `c` satisfies the compiler but carries no cancellation, so a call that honors `context.Context` will never be interrupted. Derive from [`Context`](#context) and pass that instead: ::: ```go title="Example" app.Get("/", func(c fiber.Ctx) error { ctx, cancel := context.WithTimeout(c.Context(), 5*time.Second) defer cancel() // The driver respects the 5s timeout. Passing c would never time out. rows, err := db.QueryContext(ctx, "SELECT ...") if err != nil { return err } defer rows.Close() // ... return nil }) ``` [`SetContext`](#setcontext) replaces what `Context()` returns for the rest of the request, so downstream middleware and handlers observe it. It does not make `c` itself cancelable. #### Value Value can be used to retrieve [**`Locals`**](#locals). ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Locals(userKey, "admin") user := c.Value(userKey) // returns "admin" }) ``` ### Drop Terminates the client connection silently without sending any HTTP headers or response body. This can be used for scenarios where you want to block certain requests without notifying the client, such as mitigating DDoS attacks or protecting sensitive endpoints from unauthorized access. ```go title="Signature" func (c fiber.Ctx) Drop() error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { if c.IP() == "192.168.1.1" { return c.Drop() } return c.SendString("Hello World!") }) ``` ### FullPath Returns the full path of the matched route. This includes any prefixes that were added by [groups](../guide/routing.md#grouping) or mounts. ```go title="Signature" func (c fiber.Ctx) FullPath() string ``` ```go title="Example" api := app.Group("/api") api.Get("/users/:id", func(c fiber.Ctx) error { return c.JSON(fiber.Map{ "route": c.FullPath(), // "/api/users/:id" }) }) app.Use(func(c fiber.Ctx) error { beforeNext := c.FullPath() // "/" if err := c.Next(); err != nil { return err } afterNext := c.FullPath() // "/api/users/:id" // ... react to the downstream handler's route path return nil }) ``` ### GetReqHeaders Returns the HTTP request headers as a map. Because a header can appear multiple times in a request, each key maps to a slice with all values for that header. ```go title="Signature" func (c fiber.Ctx) GetReqHeaders() map[string][]string ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### GetRespHeader Returns the HTTP response header specified by the field. :::tip The match is **case-insensitive**. ::: ```go title="Signature" func (c fiber.Ctx) GetRespHeader(key string, defaultValue ...string) string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.GetRespHeader("X-Request-Id") // "8d7ad5e3-aaf3-450b-a241-2beb887efd54" c.GetRespHeader("Content-Type") // "text/plain" c.GetRespHeader("something", "john") // "john" // .. }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### GetRespHeaders Returns the HTTP response headers as a map. Since a header can be set multiple times in a single request, the values of the map are slices of strings containing all the different values of the header. ```go title="Signature" func (c fiber.Ctx) GetRespHeaders() map[string][]string ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### GetRouteURL Generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831" ```go title="Signature" func (c fiber.Ctx) GetRouteURL(routeName string, params Map) (string, error) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Home page") }).Name("home") app.Get("/user/:id", func(c fiber.Ctx) error { return c.SendString(c.Params("id")) }).Name("user.show") app.Get("/test", func(c fiber.Ctx) error { location, _ := c.GetRouteURL("user.show", fiber.Map{"id": 1}) return c.SendString(location) }) // /test returns "/user/1" ``` ### HasBody Returns `true` if the incoming request contains a body or a `Content-Length` header greater than zero. ```go title="Signature" func (c fiber.Ctx) HasBody() bool ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { if !c.HasBody() { return c.SendStatus(fiber.StatusBadRequest) } return c.SendString("OK") }) ``` ### IsMiddleware Returns `true` if the current request handler was registered as middleware. ```go title="Signature" func (c fiber.Ctx) IsMiddleware() bool ``` ```go title="Example" app.Get("/route", func(c fiber.Ctx) error { fmt.Println(c.IsMiddleware()) // true return c.Next() }, func(c fiber.Ctx) error { fmt.Println(c.IsMiddleware()) // false return c.SendStatus(fiber.StatusOK) }) ``` ### IsPreflight Returns `true` if the request is a CORS preflight (`OPTIONS` + `Access-Control-Request-Method` + `Origin`). ```go title="Signature" func (c fiber.Ctx) IsPreflight() bool ``` ```go title="Example" app.Use(func(c fiber.Ctx) error { if c.IsPreflight() { return c.SendStatus(fiber.StatusNoContent) } return c.Next() }) ``` ### IsWebSocket Returns `true` if the request includes a WebSocket upgrade handshake. ```go title="Signature" func (c fiber.Ctx) IsWebSocket() bool ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { if c.IsWebSocket() { // handle websocket } return c.Next() }) ``` ### Locals Stores variables scoped to the request, making them available only to matching routes. The variables are removed after the request completes. If a stored value implements `io.Closer`, Fiber calls its `Close` method before removal. :::tip This is useful if you want to pass some **specific** data to the next middleware. Remember to perform type assertions when retrieving the data to ensure it is of the expected type. You can also use a non-exported type as a key to avoid collisions. ::: ```go title="Signature" func (c fiber.Ctx) Locals(key any, value ...any) any ``` ```go title="Example" // keyType is an unexported type for keys defined in this package. // This prevents collisions with keys defined in other packages. type keyType int // userKey is the key for user.User values in Contexts. It is // unexported; clients use user.NewContext and user.FromContext // instead of using this key directly. var userKey keyType app.Use(func(c fiber.Ctx) error { c.Locals(userKey, "admin") // Stores the string "admin" under a non-exported type key return c.Next() }) app.Get("/admin", func(c fiber.Ctx) error { user, ok := c.Locals(userKey).(string) // Retrieves the data stored under the key and performs a type assertion if ok && user == "admin" { return c.Status(fiber.StatusOK).SendString("Welcome, admin!") } return c.SendStatus(fiber.StatusForbidden) }) ``` An alternative version of the `Locals` method that takes advantage of Go's generics feature is also available. This version allows for the manipulation and retrieval of local values within a request's context with a more specific data type. ```go title="Signature" func Locals[V any](c fiber.Ctx, key any, value ...V) V ``` ```go title="Example" app.Use(func(c fiber.Ctx) error { fiber.Locals[string](c, "john", "doe") fiber.Locals[int](c, "age", 18) fiber.Locals[bool](c, "isHuman", true) return c.Next() }) app.Get("/test", func(c fiber.Ctx) error { fiber.Locals[string](c, "john") // "doe" fiber.Locals[int](c, "age") // 18 fiber.Locals[bool](c, "isHuman") // true return nil }) ``` Make sure to understand and correctly implement the `Locals` method in both its standard and generic form for better control over route-specific data within your application. ### Matched Returns `true` if the current request path was matched by the router. ```go title="Signature" func (c fiber.Ctx) Matched() bool ``` ```go title="Example" app.Use(func(c fiber.Ctx) error { if c.Matched() { return c.Next() } return c.Status(fiber.StatusNotFound).SendString("Not Found") }) ``` ### Next When **Next** is called, it executes the next method in the stack that matches the current route. You can pass an error struct within the method that will end the chaining and call the [error handler](../guide/error-handling). ```go title="Signature" func (c fiber.Ctx) Next() error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { fmt.Println("1st route!") return c.Next() }) app.Get("*", func(c fiber.Ctx) error { fmt.Println("2nd route!") return c.Next() }) app.Get("/", func(c fiber.Ctx) error { fmt.Println("3rd route!") return c.SendString("Hello, World!") }) ``` ### OverrideParam Overwrites the value of an existing route parameter. :::note If the parameter does not exist, this method does nothing. ::: ```go title="Signature" func (c fiber.Ctx) OverrideParam(name, value string) ``` ```go title="Example" // GET http://example.com/user app.Get("/user/:name", func(c fiber.Ctx) error { // mutate parameter c.OverrideParam("name", "new value") return c.SendString(c.Params("name")) // sends "new value" }) // GET http://example.com/shop/tech/1 app.Get("/shop/*", func(c fiber.Ctx) error { // mutate parameter c.OverrideParam("*", "new tech") // replaces "tech/1" with "new tech" return c.SendString(c.Params("*")) // sends "new tech" }) ``` Unnamed route parameters can be accessed by their character (`*` or `+`) followed by their position index (e.g., `*1` for the first wildcard, `*2` for the second). ```go title="Example" // GET /v1/brand/4/shop/blue/xs app.Get("/v1/*/shop/*", func(c fiber.Ctx) error { // mutate parameter c.OverrideParam("*1", "updated brand") c.OverrideParam("*2", "updated data") param1 := c.Params("*1") // "updated brand" param2 := c.Params("*2") // "updated data" // ... }) ``` ### Redirect Returns the Redirect reference. For detailed information, check the [Redirect](./redirect.md) documentation. ```go title="Signature" func (c fiber.Ctx) Redirect() *Redirect ``` ```go title="Example" app.Get("/coffee", func(c fiber.Ctx) error { return c.Redirect().To("/teapot") }) app.Get("/teapot", func(c fiber.Ctx) error { return c.Status(fiber.StatusTeapot).Send("🍵 short and stout 🍵") }) ``` ### Request Returns the [*fasthttp.Request](https://pkg.go.dev/github.com/valyala/fasthttp#Request) pointer. ```go title="Signature" func (c fiber.Ctx) Request() *fasthttp.Request ``` :::info Returns `nil` if the context has been released (e.g., after the handler completes and the context is returned to the pool). ::: ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Request().Header.Method() // => []byte("GET") }) ``` ### RequestCtx Returns [\*fasthttp.RequestCtx](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) that is compatible with the `context.Context` interface that requires a deadline, a cancellation signal, and other values across API boundaries. ```go title="Signature" func (c fiber.Ctx) RequestCtx() *fasthttp.RequestCtx ``` :::info Please read the [Fasthttp Documentation](https://pkg.go.dev/github.com/valyala/fasthttp?tab=doc) for more information. ::: ### Reset Resets the context fields by the given request when using server handlers. ```go title="Signature" func (c fiber.Ctx) Reset(fctx *fasthttp.RequestCtx) ``` It is used outside of the Fiber Handlers to reset the context for the next request. ### Response Returns the [\*fasthttp.Response](https://pkg.go.dev/github.com/valyala/fasthttp#Response) pointer. ```go title="Signature" func (c fiber.Ctx) Response() *fasthttp.Response ``` :::info Returns `nil` if the context has been released (e.g., after the handler completes and the context is returned to the pool). ::: ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Response().BodyWriter().Write([]byte("Hello, World!")) // => "Hello, World!" return nil }) ``` ### RestartRouting Instead of executing the next method when calling [Next](ctx.md#next), **RestartRouting** restarts execution from the first method that matches the current route. This may be helpful after overriding the path, i.e., an internal redirect. Note that handlers might be executed again, which could result in an infinite loop. ```go title="Signature" func (c fiber.Ctx) RestartRouting() error ``` ```go title="Example" app.Get("/new", func(c fiber.Ctx) error { return c.SendString("From /new") }) app.Get("/old", func(c fiber.Ctx) error { c.Path("/new") return c.RestartRouting() }) ``` ### Route Returns the matched [Route](https://pkg.go.dev/github.com/gofiber/fiber?tab=doc#Route) struct. ```go title="Signature" func (c fiber.Ctx) Route() *Route ``` ```go title="Example" // http://localhost:8080/hello app.Get("/hello/:name", func(c fiber.Ctx) error { r := c.Route() fmt.Println(r.Method, r.Path, r.Params, r.Handlers) // GET /hello/:name handler [name] // ... }) ``` :::caution Do not rely on `c.Route()` in middlewares **before** calling `c.Next()` - `c.Route()` returns the **last executed route**. ::: ```go title="Example" func MyMiddleware() fiber.Handler { return func(c fiber.Ctx) error { beforeNext := c.Route().Path // Will be '/' err := c.Next() afterNext := c.Route().Path // Will be '/hello/:name' return err } } ``` ### SetContext Sets the base `context.Context` used by [`Context`](#context). Use this to propagate deadlines, cancellation signals, or values to asynchronous operations. ```go title="Signature" func (c fiber.Ctx) SetContext(ctx context.Context) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.SetContext(context.WithValue(context.Background(), "user", "alice")) ctx := c.Context() go doWork(ctx) return nil }) ``` ### String Returns a unique string representation of the context. ```go title="Signature" func (c fiber.Ctx) String() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.String() // => "#0000000100000001 - 127.0.0.1:3000 <-> 127.0.0.1:61516 - GET http://localhost:3000/" // ... }) ``` ### ViewBind Adds variables to the default view variable map binding to the template engine. Variables are read by the `Render` method and may be overwritten. ```go title="Signature" func (c fiber.Ctx) ViewBind(vars Map) error ``` ```go title="Example" app.Use(func(c fiber.Ctx) error { c.ViewBind(fiber.Map{ "Title": "Hello, World!", }) return c.Next() }) app.Get("/", func(c fiber.Ctx) error { return c.Render("xxx.tmpl", fiber.Map{}) // Render will use the Title variable }) ``` ## Request Methods which operate on the incoming request. :::tip Use `c.Req()` to limit gopls suggestions to only these methods! ::: ### AcceptEncoding Returns the `Accept-Encoding` request header. ```go title="Signature" func (c fiber.Ctx) AcceptEncoding() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.AcceptEncoding() // "gzip, br" return nil }) ``` ### AcceptLanguage Returns the `Accept-Language` request header. ```go title="Signature" func (c fiber.Ctx) AcceptLanguage() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.AcceptLanguage() // "en-US,en;q=0.9" return nil }) ``` ### Accepts Checks if the specified **extensions** or **content** **types** are acceptable. :::info Based on the request’s [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. ::: ```go title="Signature" func (c fiber.Ctx) Accepts(offers ...string) string func (c fiber.Ctx) AcceptsCharsets(offers ...string) string func (c fiber.Ctx) AcceptsEncodings(offers ...string) string func (c fiber.Ctx) AcceptsLanguages(offers ...string) string func (c fiber.Ctx) AcceptsLanguagesExtended(offers ...string) string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Accepts("html") // "html" c.Accepts("text/html") // "text/html" c.Accepts("json", "text") // "json" c.Accepts("application/json") // "application/json" c.Accepts("text/plain", "application/json") // "application/json", due to quality c.Accepts("image/png") // "" c.Accepts("png") // "" // ... }) ``` ```go title="Example 2" // Accept: text/html, text/*, application/json, */*; q=0 app.Get("/", func(c fiber.Ctx) error { c.Accepts("text/plain", "application/json") // "application/json", due to specificity c.Accepts("application/json", "text/html") // "text/html", due to first match c.Accepts("image/png") // "", due to */* with q=0 is Not Acceptable // ... }) ``` Media-Type parameters are supported. ```go title="Example 3" // Accept: text/plain, application/json; version=1; foo=bar app.Get("/", func(c fiber.Ctx) error { // Extra parameters in the accept are ignored c.Accepts("text/plain;format=flowed") // "text/plain;format=flowed" // An offer must contain all parameters present in the Accept type c.Accepts("application/json") // "" // Parameter order and capitalization do not matter. Quotes on values are stripped. c.Accepts(`application/json;foo="bar";VERSION=1`) // "application/json;foo="bar";VERSION=1" }) ``` ```go title="Example 4" // Accept: text/plain;format=flowed;q=0.9, text/plain // i.e., "I prefer text/plain;format=flowed less than other forms of text/plain" app.Get("/", func(c fiber.Ctx) error { // Beware: the order in which offers are listed matters. // Although the client specified they prefer not to receive format=flowed, // the text/plain Accept matches with "text/plain;format=flowed" first, so it is returned. c.Accepts("text/plain;format=flowed", "text/plain") // "text/plain;format=flowed" // Here, things behave as expected: c.Accepts("text/plain", "text/plain;format=flowed") // "text/plain" }) ``` Fiber provides similar functions for the other accept headers. For `Accept-Language`, Fiber uses the [Basic Filtering](https://www.rfc-editor.org/rfc/rfc4647#section-3.3.1) algorithm. A language range matches an offer only if it exactly equals the tag or is a prefix followed by a hyphen. For example, the range `en` matches `en-US`, but `en-US` does not match `en`. `AcceptsLanguagesExtended` applies [Extended Filtering](https://www.rfc-editor.org/rfc/rfc4647#section-3.3.2) where `*` may match zero or more subtags and wildcard matches can slide across subtags unless blocked by a singleton like `x`. ```go // Accept-Charset: utf-8, iso-8859-1;q=0.2 // Accept-Encoding: gzip, compress;q=0.2 // Accept-Language: en;q=0.8, nl, ru app.Get("/", func(c fiber.Ctx) error { c.AcceptsCharsets("utf-16", "iso-8859-1") // "iso-8859-1" c.AcceptsEncodings("compress", "br") // "compress" c.AcceptsLanguages("pt", "nl", "ru") // "nl" c.AcceptsLanguagesExtended("en-US", "fr-CA") // depends on extended ranges in the request header // ... }) ``` ### AcceptsEventStream Returns `true` when the `Accept` header allows `text/event-stream`. ```go title="Signature" func (c fiber.Ctx) AcceptsEventStream() bool ``` ```go title="Example" // Accept: text/html, application/json;q=0.9 app.Get("/", func(c fiber.Ctx) error { c.AcceptsEventStream() // false return nil }) ``` ### AcceptsHTML Returns `true` when the `Accept` header allows HTML. ```go title="Signature" func (c fiber.Ctx) AcceptsHTML() bool ``` ```go title="Example" // Accept: text/html, application/json;q=0.9 app.Get("/", func(c fiber.Ctx) error { c.AcceptsHTML() // true return nil }) ``` ### AcceptsJSON Returns `true` when the `Accept` header allows JSON. ```go title="Signature" func (c fiber.Ctx) AcceptsJSON() bool ``` ```go title="Example" // Accept: text/html, application/json;q=0.9 app.Get("/", func(c fiber.Ctx) error { c.AcceptsJSON() // true return nil }) ``` ### AcceptsXML Returns `true` when the `Accept` header allows XML. ```go title="Signature" func (c fiber.Ctx) AcceptsXML() bool ``` ```go title="Example" // Accept: text/html, application/json;q=0.9 app.Get("/", func(c fiber.Ctx) error { c.AcceptsXML() // false return nil }) ``` ### BaseURL Returns the base URL (**protocol** + **host**) as a `string`. ```go title="Signature" func (c fiber.Ctx) BaseURL() string ``` ```go title="Example" // GET https://example.com/page#chapter-1 app.Get("/", func(c fiber.Ctx) error { c.BaseURL() // "https://example.com" // ... }) ``` ### Body As per the header `Content-Encoding`, this method will try to perform a file decompression from the **body** bytes. In case no `Content-Encoding` header is sent (or when it is set to `identity`), it will perform as [BodyRaw](#bodyraw). If an unknown or unsupported encoding is encountered, the response status will be `415 Unsupported Media Type` or `501 Not Implemented`. Decompression is bounded by the app [BodyLimit](./fiber.md#bodylimit). ```go title="Signature" func (c fiber.Ctx) Body() []byte ``` ```go title="Example" // echo 'user=john' | gzip | curl -v -i --data-binary @- -H "Content-Encoding: gzip" http://localhost:8080 app.Post("/", func(c fiber.Ctx) error { // Decompress body from POST request based on the Content-Encoding and return the raw content: return c.Send(c.Body()) // []byte("user=john") }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### BodyRaw Returns the raw request **body**. ```go title="Signature" func (c fiber.Ctx) BodyRaw() []byte ``` ```go title="Example" // curl -X POST http://localhost:8080 -d user=john app.Post("/", func(c fiber.Ctx) error { // Get raw body from POST request: return c.Send(c.BodyRaw()) // []byte("user=john") }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### Charset Returns the `charset` parameter from the `Content-Type` header. ```go title="Signature" func (c fiber.Ctx) Charset() string ``` ```go title="Example" // Content-Type: application/json; charset=utf-8 app.Post("/", func(c fiber.Ctx) error { c.Charset() // "utf-8" return nil }) ``` ### ClientHelloInfo `ClientHelloInfo` contains information from a ClientHello message to guide application logic in the `GetCertificate` and `GetConfigForClient` callbacks. Refer to the [ClientHelloInfo](https://golang.org/pkg/crypto/tls/#ClientHelloInfo) struct documentation for details on the returned struct. ```go title="Signature" func (c fiber.Ctx) ClientHelloInfo() *tls.ClientHelloInfo ``` ```go title="Example" // GET http://example.com/hello app.Get("/hello", func(c fiber.Ctx) error { chi := c.ClientHelloInfo() // ... }) ``` ### Cookies Gets a cookie value by key. You can pass an optional default value that will be returned if the cookie key does not exist. ```go title="Signature" func (c fiber.Ctx) Cookies(key string, defaultValue ...string) string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // Get cookie by key: c.Cookies("name") // "john" c.Cookies("empty", "doe") // "doe" // ... }) ``` :::info The returned value is valid only within the handler. Do not store references. Use [`App.GetString`](./app.md#getstring) or [`App.GetBytes`](./app.md#getbytes) when immutability is enabled, or manually copy values (for example with [`utils.CopyString`](https://github.com/gofiber/utils) / `utils.CopyBytes`) when it's disabled. [Read more...](../#zero-allocation) ::: ### FormFile MultipartForm files can be retrieved by name, the **first** file from the given key is returned. ```go title="Signature" func (c fiber.Ctx) FormFile(key string) (*multipart.FileHeader, error) ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { // Get first file from form field "document": file, err := c.FormFile("document") // Save file to root directory: return c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)) }) ``` ### FormValue Form values can be retrieved by name, the **first** value for the given key is returned. ```go title="Signature" func (c fiber.Ctx) FormValue(key string, defaultValue ...string) string ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { // Get first value from form field "name": c.FormValue("name") // => "john" or "" if not exist // .. }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### Fresh When the response is still **fresh** in the client's cache **true** is returned; otherwise, **false** is returned to indicate that the client cache is now stale and the full response should be sent. When a client sends the Cache-Control: no-cache request header to indicate an end-to-end reload request, `Fresh` will return false to make handling these requests transparent. `Fresh` only applies to GET and HEAD requests and returns false for any other method, since a 304 Not Modified response is only defined for those methods and RFC 9110 requires If-Modified-Since to be ignored otherwise. Read more on [https://expressjs.com/en/4x/api.html\#req.fresh](https://expressjs.com/en/4x/api.html#req.fresh) ```go title="Signature" func (c fiber.Ctx) Fresh() bool ``` ### FullURL Returns the full request URL (protocol + host + original URL). ```go title="Signature" func (c fiber.Ctx) FullURL() string ``` ```go title="Example" // GET http://example.com/search?q=fiber app.Get("/", func(c fiber.Ctx) error { c.FullURL() // "http://example.com/search?q=fiber" return nil }) ``` ### Get Returns the HTTP request header specified by the field. :::tip The match is **case-insensitive**. ::: ```go title="Signature" func (c fiber.Ctx) Get(key string, defaultValue ...string) string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Get("Content-Type") // "text/plain" c.Get("CoNtEnT-TypE") // "text/plain" c.Get("something", "john") // "john" // .. }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### HasHeader Reports whether the request includes a header with the given key. ```go title="Signature" func (c fiber.Ctx) HasHeader(key string) bool ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.HasHeader("X-Trace-Id") return nil }) ``` ### Host Returns the host derived from the [Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host) HTTP header. In a network context, [`Host`](#host) refers to the combination of a hostname and potentially a port number used for connecting, while [`Hostname`](#hostname) refers specifically to the name assigned to a device on a network, excluding any port information. ```go title="Signature" func (c fiber.Ctx) Host() string ``` ```go title="Example" // GET http://google.com:8080/search app.Get("/", func(c fiber.Ctx) error { c.Host() // "google.com:8080" c.Hostname() // "google.com" // ... }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### Hostname Returns the hostname derived from the [Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host) HTTP header. ```go title="Signature" func (c fiber.Ctx) Hostname() string ``` ```go title="Example" // GET http://google.com/search app.Get("/", func(c fiber.Ctx) error { c.Hostname() // "google.com" // ... }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### IP Returns the remote IP address of the request. ```go title="Signature" func (c fiber.Ctx) IP() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.IP() // "127.0.0.1" // ... }) ``` :::info By default, `c.IP()` returns the remote IP address from the TCP connection. When your Fiber app is behind a reverse proxy (like Nginx, Traefik, or a load balancer), you need to configure **both** [`TrustProxy`](fiber.md#trustproxy) and [`ProxyHeader`](fiber.md#proxyheader) to read the client IP from proxy headers like `X-Forwarded-For`. **Important:** You must enable `TrustProxy` and configure trusted proxy IPs to prevent header spoofing. Simply setting `ProxyHeader` alone will not work. **Note:** When using a proxy header such as `X-Forwarded-For`, `c.IP()` returns the raw header value unless [`EnableIPValidation`](fiber.md#enableipvalidation) is enabled. **Chain parsing with `EnableIPValidation`:** For `X-Forwarded-For`, the raw value is a comma-separated chain that grows from left to right as the request passes through each proxy. With validation enabled, `c.IP()` walks the chain from right to left, skipping every IP that matches the configured `TrustProxyConfig` (exact IPs, CIDR ranges, loopback, private or link-local) and returns the first non-trusted IP it finds. This matches the behavior recommended by [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For#selecting_an_ip_address) and the convention used by Nginx (`set_real_ip_from` + `real_ip_recursive`), Apache `mod_remoteip`, and Envoy (`xff_num_trusted_hops`). If every IP in the chain matches the trusted set, the leftmost IP is returned as a fallback. If the chain is empty, `c.IP()` falls back to the TCP remote address. ::: #### Configuration for apps behind a reverse proxy ```go title="Example - Basic Configuration" app := fiber.New(fiber.Config{ // Enable proxy support TrustProxy: true, // Specify which header contains the real client IP ProxyHeader: fiber.HeaderXForwardedFor, // Configure which proxy IPs to trust TrustProxyConfig: fiber.TrustProxyConfig{ // Trust private IP ranges (for internal load balancers) Private: true, // Or specify exact proxy IPs/ranges // Proxies: []string{"10.10.0.58", "192.168.0.0/24"}, }, }) ``` ```go title="Example - Specific Proxy IPs" app := fiber.New(fiber.Config{ TrustProxy: true, ProxyHeader: fiber.HeaderXForwardedFor, TrustProxyConfig: fiber.TrustProxyConfig{ // Trust only specific proxy IP addresses Proxies: []string{"10.10.0.58", "192.168.1.0/24"}, }, }) ``` See [`TrustProxy`](fiber.md#trustproxy) and [`TrustProxyConfig`](fiber.md#trustproxyconfig) for more details on security considerations and configuration options. ### IPs Returns an array of IP addresses specified in the [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) request header. ```go title="Signature" func (c fiber.Ctx) IPs() []string ``` ```go title="Example" // X-Forwarded-For: proxy1, 127.0.0.1, proxy3 app.Get("/", func(c fiber.Ctx) error { c.IPs() // ["proxy1", "127.0.0.1", "proxy3"] // ... }) ``` :::caution Improper use of the X-Forwarded-For header can be a security risk. For details, see the [Security and privacy concerns](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#security_and_privacy_concerns) section. ::: ### Is Returns the matching **content type**, if the incoming request’s [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) HTTP header field matches the [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) specified by the type parameter. :::info If the request has **no** body, it returns **false**. ::: ```go title="Signature" func (c fiber.Ctx) Is(extension string) bool ``` ```go title="Example" // Content-Type: text/html; charset=utf-8 app.Get("/", func(c fiber.Ctx) error { c.Is("html") // true c.Is(".html") // true c.Is("json") // false // ... }) ``` ### IsForm Reports whether the `Content-Type` header is form-encoded. ```go title="Signature" func (c fiber.Ctx) IsForm() bool ``` ```go title="Example" // Content-Type: application/x-www-form-urlencoded app.Post("/", func(c fiber.Ctx) error { c.IsForm() // true return nil }) ``` ### IsFromLocal Returns `true` if the request came from localhost. ```go title="Signature" func (c fiber.Ctx) IsFromLocal() bool ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // If request came from localhost, return true; else return false c.IsFromLocal() // ... }) ``` ### IsFromUnixSocket Returns `true` if the request came in over a Unix domain socket. ```go title="Signature" func (c fiber.Ctx) IsFromUnixSocket() bool ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { if c.IsFromUnixSocket() { return c.SendString("Connected via Unix socket") } return c.SendString("Connected via TCP") }) ``` ### IsJSON Reports whether the `Content-Type` header is JSON. ```go title="Signature" func (c fiber.Ctx) IsJSON() bool ``` ```go title="Example" // Content-Type: application/json; charset=utf-8 app.Post("/", func(c fiber.Ctx) error { c.IsJSON() // true return nil }) ``` ### IsMultipart Reports whether the `Content-Type` header is multipart form data. ```go title="Signature" func (c fiber.Ctx) IsMultipart() bool ``` ```go title="Example" // Content-Type: multipart/form-data; boundary=abc123 app.Post("/", func(c fiber.Ctx) error { c.IsMultipart() // true return nil }) ``` ### IsProxyTrusted Checks the trustworthiness of the remote IP. If [`TrustProxy`](fiber.md#trustproxy) is `false`, it returns `true`. `IsProxyTrusted` can check the remote IP by proxy ranges and IP map. ```go title="Signature" func (c fiber.Ctx) IsProxyTrusted() bool ``` ```go title="Example" app := fiber.New(fiber.Config{ // TrustProxy enables the trusted proxy check TrustProxy: true, // TrustProxyConfig allows for configuring trusted proxies. // Proxies is a list of trusted proxy IP ranges/addresses TrustProxyConfig: fiber.TrustProxyConfig{ Proxies: []string{"0.8.0.0", "1.1.1.1/30"}, // IP address or IP address range Loopback: true, // Trust loopback addresses (127.0.0.0/8, ::1/128) UnixSocket: true, // Trust Unix domain socket connections }, }) app.Get("/", func(c fiber.Ctx) error { // If request came from trusted proxy, return true; else return false c.IsProxyTrusted() // ... }) ``` ### MediaType Returns the MIME type from the `Content-Type` header without parameters. ```go title="Signature" func (c fiber.Ctx) MediaType() string ``` ```go title="Example" // Content-Type: application/json; charset=utf-8 app.Post("/", func(c fiber.Ctx) error { c.MediaType() // "application/json" return nil }) ``` ### Method Returns a string corresponding to the HTTP method of the request: `GET`, `POST`, `PUT`, and so on. Optionally, you can override the method by passing a string. Method tokens are case-sensitive (RFC 9110): the override is first matched exactly against the methods registered in [`Config.RequestMethods`](./fiber.md#requestmethods), and only falls back to the uppercase form as a convenience for the standard methods (e.g. `"get"` → `GET`). An unregistered override is ignored. :::caution Route registration (`app.Get`, `app.Add`, …) uppercases method names before validating them, so custom methods you want to **route** must be registered in `Config.RequestMethods` in uppercase. A mixed-case entry can be set via `c.Method(...)` but cannot have routes registered for it. ::: ```go title="Signature" func (c fiber.Ctx) Method(override ...string) string ``` ```go title="Example" app.Post("/override", func(c fiber.Ctx) error { c.Method() // "POST" c.Method("GET") c.Method() // "GET" // ... }) ``` ### MultipartForm To access multipart form entries, you can parse the binary with `MultipartForm()`. This returns a `*multipart.Form`, allowing you to access form values and files. Parsing is bounded by the app [BodyLimit](./fiber.md#bodylimit). ```go title="Signature" func (c fiber.Ctx) MultipartForm() (*multipart.Form, error) ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form if token := form.Value["token"]; len(token) > 0 { // Get key value: fmt.Println(token[0]) } // Get all files from "documents" key: files := form.File["documents"] // => []*multipart.FileHeader // Loop through files: for _, file := range files { fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) // => "tutorial.pdf" 360641 "application/pdf" // Save the files to disk: if err := c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)); err != nil { return err } } } return nil }) ``` ### OriginalURL Returns the original request URL. ```go title="Signature" func (c fiber.Ctx) OriginalURL() string ``` ```go title="Example" // GET http://example.com/search?q=something app.Get("/", func(c fiber.Ctx) error { c.OriginalURL() // "/search?q=something" // ... }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: ### Params This method can be used to get the route parameters. You can pass an optional default value that will be returned if the param key does not exist. :::info Defaults to an empty string \(`""`\) if the param **doesn't** exist. ::: ```go title="Signature" func (c fiber.Ctx) Params(key string, defaultValue ...string) string ``` ```go title="Example" // GET http://example.com/user/fenny app.Get("/user/:name", func(c fiber.Ctx) error { c.Params("name") // "fenny" // ... }) // GET http://example.com/user/fenny/123 app.Get("/user/*", func(c fiber.Ctx) error { c.Params("*") // "fenny/123" c.Params("*1") // "fenny/123" // ... }) ``` Unnamed route parameters \(\*, +\) can be fetched by the **character** and the **counter** in the route. ```go title="Example" // ROUTE: /v1/*/shop/* // GET: /v1/brand/4/shop/blue/xs c.Params("*1") // "brand/4" c.Params("*2") // "blue/xs" ``` For reasons of **downward compatibility**, the first parameter segment for the parameter character can also be accessed without the counter. ```go title="Example" app.Get("/v1/*/shop/*", func(c fiber.Ctx) error { c.Params("*") // outputs the value of the first wildcard segment }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: In certain scenarios, it can be useful to have an alternative approach to handle different types of parameters, not just strings. This can be achieved using a generic `Params` function known as `Params[V GenericType](c fiber.Ctx, key string, defaultValue ...V) V`. This function is capable of parsing a route parameter and returning a value of a type that is assumed and specified by `V GenericType`. ```go title="Signature" func Params[V GenericType](c fiber.Ctx, key string, defaultValue ...V) V ``` ```go title="Example" // GET http://example.com/user/114 app.Get("/user/:id", func(c fiber.Ctx) error{ fiber.Params[string](c, "id") // returns "114" as string. fiber.Params[int](c, "id") // returns 114 as integer fiber.Params[string](c, "number") // returns "" (default string type) fiber.Params[int](c, "number") // returns 0 (default integer value type) }) ``` The generic `Params` function supports returning the following data types based on `V GenericType`: - Integer: `int`, `int8`, `int16`, `int32`, `int64` - Unsigned integer: `uint`, `uint8`, `uint16`, `uint32`, `uint64` - Floating-point numbers: `float32`, `float64` - Boolean: `bool` - String: `string` - Byte array: `[]byte` ### Path Contains the path part of the request URL. Optionally, you can override the path by passing a string. For internal redirects, you might want to call [RestartRouting](ctx.md#restartrouting) instead of [Next](ctx.md#next). ```go title="Signature" func (c fiber.Ctx) Path(override ...string) string ``` ```go title="Example" // GET http://example.com/users?sort=desc app.Get("/users", func(c fiber.Ctx) error { c.Path() // "/users" c.Path("/john") c.Path() // "/john" // ... }) ``` ### Port Returns the remote port of the request. ```go title="Signature" func (c fiber.Ctx) Port() string ``` ```go title="Example" // GET http://example.com:8080 app.Get("/", func(c fiber.Ctx) error { c.Port() // "8080" // ... }) ``` ### Protocol Returns the HTTP protocol version of the request: `HTTP/1.1` or `HTTP/2`. :::info To get the request scheme (`http` or `https`), use [`Scheme`](#scheme) instead. ::: ```go title="Signature" func (c fiber.Ctx) Protocol() string ``` ```go title="Example" // GET http://example.com app.Get("/", func(c fiber.Ctx) error { c.Protocol() // "HTTP/1.1" // ... }) ``` ### Queries `Queries` is a function that returns an object containing a property for each query string parameter in the route. ```go title="Signature" func (c fiber.Ctx) Queries() map[string]string ``` ```go title="Example" // GET http://example.com/?name=alex&want_pizza=false&id= app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["name"] // "alex" m["want_pizza"] // "false" m["id"] // "" // ... }) ``` ```go title="Example" // GET http://example.com/?field1=value1&field1=value2&field2=value3 app.Get("/", func (c fiber.Ctx) error { m := c.Queries() m["field1"] // "value2" m["field2"] // "value3" }) ``` ```go title="Example" // GET http://example.com/?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3 app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["list_a"] // "3" m["list_b[]"] // "3" m["list_c"] // "1,2,3" }) ``` ```go title="Example" // GET /api/posts?filters.author.name=John&filters.category.name=Technology app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["filters.author.name"] // John m["filters.category.name"] // Technology }) ``` ```go title="Example" // GET /api/posts?tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["tags"] // apple,orange,banana m["filters[tags]"] // apple,orange,banana m["filters[category][name]"] // fruits m["filters.tags"] // apple,orange,banana m["filters.category.name"] // fruits }) ``` ### Query This method returns a string corresponding to a query string parameter by name. You can pass an optional default value that will be returned if the query key does not exist. :::info If there is **no** query string, it returns an **empty string**. ::: ```go title="Signature" func (c fiber.Ctx) Query(key string, defaultValue ...string) string ``` ```go title="Example" // GET http://example.com/?order=desc&brand=nike app.Get("/", func(c fiber.Ctx) error { c.Query("order") // "desc" c.Query("brand") // "nike" c.Query("empty", "nike") // "nike" // ... }) ``` :::info The returned value is valid only within the handler. Do not store references. Make copies or use the [**`Immutable`**](./fiber.md#immutable) setting instead. [Read more...](../#zero-allocation) ::: In certain scenarios, it can be useful to have an alternative approach to handle different types of query parameters, not just strings. This can be achieved using a generic `Query` function known as `Query[V GenericType](c fiber.Ctx, key string, defaultValue ...V) V`. This function is capable of parsing a query string and returning a value of a type that is assumed and specified by `V GenericType`. Here is the signature for the generic `Query` function: ```go title="Signature" func Query[V GenericType](c fiber.Ctx, key string, defaultValue ...V) V ``` ```go title="Example" // GET http://example.com/?page=1&brand=nike&new=true app.Get("/", func(c fiber.Ctx) error { fiber.Query[int](c, "page") // 1 fiber.Query[string](c, "brand") // "nike" fiber.Query[bool](c, "new") // true // ... }) ``` In this case, `Query[V GenericType](c Ctx, key string, defaultValue ...V) V` can retrieve `page` as an integer, `brand` as a string, and `new` as a boolean. The function uses the appropriate parsing function for each specified type to ensure the correct type is returned. This simplifies the retrieval process of different types of query parameters, making your controller actions cleaner. The generic `Query` function supports returning the following data types based on `V GenericType`: - Integer: `int`, `int8`, `int16`, `int32`, `int64` - Unsigned integer: `uint`, `uint8`, `uint16`, `uint32`, `uint64` - Floating-point numbers: `float32`, `float64` - Boolean: `bool` - String: `string` - Byte array: `[]byte` ### Range Returns a struct containing the type and a slice of ranges. Only the canonical `bytes` unit is recognized and any optional whitespace around range specifiers will be ignored, as specified in RFC 9110. Empty list elements (e.g. `bytes=,0-5`) are ignored, though they still count toward `Config.MaxRanges`. A range with a non-numeric bound or a last position smaller than the first position invalidates the whole header and `ErrRangeMalformed` (carrying a **400 Bad Request** status) is returned, per RFC 9110. A grammatically valid range unit other than `bytes` (e.g. `pages=1-3`) returns `ErrRangeUnsupported`; RFC 9110 requires servers to **ignore** such a Range header, so treat this error as "serve the full representation", not as a failure. As a safety net the error carries a **400 Bad Request** status so that blindly propagating it does not surface as a 500. If the requested ranges are valid but none of them are satisfiable, the method automatically sets the HTTP status code to **416 Range Not Satisfiable** and populates the `Content-Range` header with the current representation size. ```go title="Signature" func (c fiber.Ctx) Range(size int64) (Range, error) ``` ```go title="Example" // Range: bytes=500-700, 700-900 app.Get("/", func(c fiber.Ctx) error { r := c.Range(1000) if r.Type == "bytes" { for _, rng := range r.Ranges { fmt.Println(rng) // [500, 700] } } }) ``` ### Referer Returns the `Referer` request header. ```go title="Signature" func (c fiber.Ctx) Referer() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Referer() // "https://example.com" return nil }) ``` ### RequestID ```go title="Signature" func (c fiber.Ctx) RequestID() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.RequestID() // "8d7ad5e3-aaf3-450b-a241-2beb887efd54" return nil }) ``` ### SaveFile Method is used to save **any** multipart file to disk. ```go title="Signature" func (c fiber.Ctx) SaveFile(fh *multipart.FileHeader, path string) error ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form // Get all files from "documents" key: files := form.File["documents"] // => []*multipart.FileHeader // Loop through files: for _, file := range files { fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) // => "tutorial.pdf" 360641 "application/pdf" // Save the files to disk: if err := c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)); err != nil { return err } } return err } }) ``` ### SaveFileToStorage Method is used to save **any** multipart file to an external storage system. ```go title="Signature" func (c fiber.Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error ``` ```go title="Example" storage := memory.New() app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form // Get all files from "documents" key: files := form.File["documents"] // => []*multipart.FileHeader // Loop through files: for _, file := range files { fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) // => "tutorial.pdf" 360641 "application/pdf" // Save the files to storage: if err := c.SaveFileToStorage(file, fmt.Sprintf("./%s", file.Filename), storage); err != nil { return err } } return err } }) ``` ### Scheme Contains the request protocol string: `http` or `https` for TLS requests. :::info Please use [`Config.TrustProxy`](fiber.md#trustproxy) to prevent header spoofing if your app is behind a proxy. ::: ```go title="Signature" func (c fiber.Ctx) Scheme() string ``` ```go title="Example" // GET http://example.com app.Get("/", func(c fiber.Ctx) error { c.Scheme() // "http" // ... }) ``` ### Secure A boolean property that is `true` if a **TLS** connection is established. ```go title="Signature" func (c fiber.Ctx) Secure() bool ``` ```go title="Example" // Secure() method is equivalent to: c.Scheme() == "https" ``` ### Stale When the client's cached response is **stale**, this method returns **true**. It is the logical complement of [`Fresh`](#fresh), which checks whether the cached representation is still valid. [https://expressjs.com/en/4x/api.html#req.stale](https://expressjs.com/en/4x/api.html#req.stale) ```go title="Signature" func (c fiber.Ctx) Stale() bool ``` ### Subdomains Returns a slice with the host’s sub-domain labels. The dot-separated parts that precede the registrable domain (`example`) and the top-level domain (ex: `com`). The `subdomain offset` (default `2`) tells Fiber how many labels, counting from the right-hand side, are always discarded. Passing an `offset` argument lets you override that value for a single call. ```go func (c fiber.Ctx) Subdomains(offset ...int) []string ``` | `offset` | Result | Meaning | | ---------------------- | --------------------------------------- | --------------------------------------------- | | *omitted* → **2** | trim 2 right-most labels | drop the registrable domain **and** the TLD | | `1` to `len(labels)-1` | trim exactly `offset` right-most labels | custom trimming of available labels | | `>= len(labels)` | **return `[]`** | offset exceeds available labels → empty slice | | `0` | **return every label** | keep the entire host unchanged | | `< 0` | **return `[]`** | negative offsets are invalid → empty slice | #### Example ```go // Host: "tobi.ferrets.example.com" app.Get("/", func(c fiber.Ctx) error { c.Subdomains() // ["tobi", "ferrets"] c.Subdomains(1) // ["tobi", "ferrets", "example"] c.Subdomains(0) // ["tobi", "ferrets", "example", "com"] c.Subdomains(-1) // [] // ... }) ``` ### UserAgent Returns the `User-Agent` request header. ```go title="Signature" func (c fiber.Ctx) UserAgent() string ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.UserAgent() // "Mozilla/5.0 ..." return nil }) ``` ### XHR A boolean property that is `true` if the request’s [X-Requested-With](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) header field is [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest), indicating that the request was issued by a client library (such as [jQuery](https://api.jquery.com/jQuery.ajax/)). ```go title="Signature" func (c fiber.Ctx) XHR() bool ``` ```go title="Example" // X-Requested-With: XMLHttpRequest app.Get("/", func(c fiber.Ctx) error { c.XHR() // true // ... }) ``` ## Response Methods which modify the response object. :::tip Use `c.Res()` to limit gopls suggestions to only these methods! ::: ### Append Appends the specified **value** to the HTTP response header field. :::caution If the header is **not** already set, it creates the header with the specified value. ::: Empty values are skipped, since a sender must not generate empty list elements (RFC 9110). ```go title="Signature" func (c fiber.Ctx) Append(field string, values ...string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Append("Link", "http://google.com", "http://localhost") // => Link: http://google.com, http://localhost c.Append("Link", "Test") // => Link: http://google.com, http://localhost, Test // ... }) ``` ### Attachment Sets the HTTP response [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header field to `attachment`. ```go title="Signature" func (c fiber.Ctx) Attachment(filename ...string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Attachment() // => Content-Disposition: attachment c.Attachment("./upload/images/logo.png") // => Content-Disposition: attachment; filename="logo.png" // => Content-Type: image/png // ... }) ``` The `filename` parameter is emitted as an RFC 9110 quoted-string: spaces and punctuation stay literal, and quotes/backslashes are escaped with a backslash (no URL encoding). Non-ASCII filenames additionally carry the `filename*` parameter as defined in [RFC 6266](https://www.rfc-editor.org/rfc/rfc6266) and [RFC 8187](https://www.rfc-editor.org/rfc/rfc8187): ```go title="Example" app.Get("/non-ascii", func(c fiber.Ctx) error { c.Attachment("./files/文件.txt") // => Content-Disposition: attachment; filename="文件.txt"; filename*=UTF-8''%E6%96%87%E4%BB%B6.txt return nil }) ``` ### AutoFormat Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format. The supported content types are `text/html`, `text/plain`, `application/json`, `application/vnd.msgpack`, `application/xml`, and `application/cbor`. Because the representation is selected from the Accept header, `Vary: Accept` is added to the response. For more flexible content negotiation, use [Format](ctx.md#format). :::info If the header is **not** specified or there is **no** proper format, **text/plain** is used. ::: ```go title="Signature" func (c fiber.Ctx) AutoFormat(body any) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // Accept: text/plain c.AutoFormat("Hello, World!") // => Hello, World! // Accept: text/html c.AutoFormat("Hello, World!") // =>

Hello, World!

type User struct { Name string } user := User{"John Doe"} // Accept: application/json c.AutoFormat(user) // => {"Name":"John Doe"} // Accept: application/vnd.msgpack c.AutoFormat(user) // => 82 a4 6e 61 6d 65 a4 6a 6f 68 6e a4 70 61 73 73 a3 64 6f 65 // Accept: application/cbor c.AutoFormat(user) // => a1 64 4e 61 6d 65 68 4a 6f 68 6e 20 44 6f 65 // Accept: application/xml c.AutoFormat(user) // => John Doe // .. }) ``` ### CBOR CBOR converts any interface or string to CBOR encoded bytes. > **Note:** Before using any CBOR-related features, make sure to follow the [CBOR setup instructions](../guide/advance-format.md#cbor). :::info CBOR also sets the content header to the `ctype` parameter. If no `ctype` is passed in, the header is set to `application/cbor`. ::: ```go title="Signature" func (c fiber.Ctx) CBOR(data any, ctype ...string) error ``` ```go title="Example" type SomeStruct struct { Name string `cbor:"name"` Age uint8 `cbor:"age"` } app.Get("/cbor", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", Age: 20, } return c.CBOR(data) // => Content-Type: application/cbor // => \xa2dnameeGramecage\x14 return c.CBOR(fiber.Map{ "name": "Grame", "age": 20, }) // => Content-Type: application/cbor // => \xa2dnameeGramecage\x14 return c.CBOR(fiber.Map{ "type": "https://example.com/probs/out-of-credit", "title": "You do not have enough credit.", "status": 403, "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc", }) // => Content-Type: application/cbor // => \xa5dtypex'https://example.com/probs/out-of-creditetitlex\x1eYou do not have enough credit.fstatus\x19\x01\x93fdetailx.Your current balance is 30, but that costs 50.hinstancew/account/12345/msgs/abc }) ``` ### ClearCookie Expires a client cookie (or all cookies if left empty). ```go title="Signature" func (c fiber.Ctx) ClearCookie(key ...string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // Clears all cookies: c.ClearCookie() // Expire specific cookie by name: c.ClearCookie("user") // Expire multiple cookies by names: c.ClearCookie("token", "session", "track_id", "version") // ... }) ``` :::caution Web browsers and other compliant clients will only clear the cookie if the given options are identical to those when creating the cookie, excluding `Expires` and `MaxAge`. `ClearCookie` will not set these values for you - a technique similar to the one shown below should be used to ensure your cookie is deleted. ::: ```go title="Example" app.Get("/set", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "token", Value: "randomvalue", Expires: time.Now().Add(24 * time.Hour), HTTPOnly: true, SameSite: "Lax", }) // ... }) app.Get("/delete", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "token", Expires: fasthttp.CookieExpireDelete, // Use fasthttp's built-in constant HTTPOnly: true, SameSite: "Lax", }) // ... }) ``` You can also use `c.Cookie()` to expire cookies with specific `Path` or `Domain` attributes: ```go title="Example" app.Get("/logout", func(c fiber.Ctx) error { // Expire a cookie with path and domain c.Cookie(&fiber.Cookie{ Name: "token", Path: "/api", Domain: "example.com", Expires: fasthttp.CookieExpireDelete, }) return c.SendStatus(fiber.StatusOK) }) ``` ### Cookie Sets a cookie. ```go title="Signature" func (c fiber.Ctx) Cookie(cookie *Cookie) ``` ```go type Cookie struct { Name string `json:"name"` // The name of the cookie Value string `json:"value"` // The value of the cookie Path string `json:"path"` // Specifies a URL path which is allowed to receive the cookie Domain string `json:"domain"` // Specifies the domain which is allowed to receive the cookie MaxAge int `json:"max_age"` // The maximum age (in seconds) of the cookie Expires time.Time `json:"expires"` // The expiration date of the cookie Secure bool `json:"secure"` // Indicates that the cookie should only be transmitted over a secure HTTPS connection HTTPOnly bool `json:"http_only"` // Indicates that the cookie is accessible only through the HTTP protocol SameSite string `json:"same_site"` // Controls whether or not a cookie is sent with cross-site requests Partitioned bool `json:"partitioned"` // Indicates if the cookie is stored in a partitioned cookie jar SessionOnly bool `json:"session_only"` // Indicates if the cookie is a session-only cookie } ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // Create cookie cookie := new(fiber.Cookie) cookie.Name = "john" cookie.Value = "doe" cookie.Expires = time.Now().Add(24 * time.Hour) // Set cookie c.Cookie(cookie) // ... }) ``` :::info When setting a cookie with `SameSite=None`, Fiber automatically sets `Secure=true` as required by RFC 6265bis and modern browsers. This ensures compliance with the "None" SameSite policy which mandates that cookies must be sent over secure connections. For more information, see: - [Mozilla Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#none) - [Chrome Documentation](https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure) ::: :::info Partitioned cookies allow partitioning the cookie jar by top-level site, enhancing user privacy by preventing cookies from being shared across different sites. This feature is particularly useful in scenarios where a user interacts with embedded third-party services that should not have access to the main site's cookies. You can check out [CHIPS](https://developers.google.com/privacy-sandbox/3pcd/chips) for more information. ::: ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // Create a new partitioned cookie cookie := new(fiber.Cookie) cookie.Name = "user_session" cookie.Value = "abc123" cookie.Partitioned = true // This cookie will be stored in a separate jar when it's embedded into another website // Set the cookie in the response c.Cookie(cookie) return c.SendString("Partitioned cookie set") }) ``` ### Download Transfers the file from the given path as an `attachment`. Typically, browsers will prompt the user to download. By default, the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header `filename=` parameter is the file path (this typically appears in the browser dialog). Override this default with the `filename` parameter. ```go title="Signature" func (c fiber.Ctx) Download(file string, filename ...string) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.Download("./files/report-12345.pdf") // => Download report-12345.pdf return c.Download("./files/report-12345.pdf", "report.pdf") // => Download report.pdf }) ``` The `filename` parameter is emitted as an RFC 9110 quoted-string (no URL encoding). For filenames containing non-ASCII characters, a `filename*` parameter is added according to [RFC 6266](https://www.rfc-editor.org/rfc/rfc6266) and [RFC 8187](https://www.rfc-editor.org/rfc/rfc8187): ```go title="Example" app.Get("/non-ascii", func(c fiber.Ctx) error { return c.Download("./files/文件.txt") // => Content-Disposition: attachment; filename="文件.txt"; filename*=UTF-8''%E6%96%87%E4%BB%B6.txt }) ``` ### End End immediately flushes the current response and closes the underlying connection. ```go title="Signature" func (c fiber.Ctx) End() error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.SendString("Hello World!") return c.End() }) ``` :::caution Calling `c.End()` will disallow further writes to the underlying connection. ::: :::warning `c.End()` does **not** work in streaming mode (e.g. when using `fasthttp`'s `HijackConn` or `SendStream`). In streaming mode the connection is managed asynchronously and `ctx.Conn()` may return `nil`, so `c.End()` will return `nil` without flushing or closing the connection. ::: End can be used to stop a middleware from modifying a response of a handler/other middleware down the method chain when they regain control after calling `c.Next()`. ```go title="Example" // Error Logging/Responding middleware app.Use(func(c fiber.Ctx) error { err := c.Next() // Log errors & write the error to the response if err != nil { log.Printf("Got error in middleware: %v", err) return c.Writef("(got error %v)", err) } // No errors occurred return nil }) // Handler with simulated error app.Get("/", func(c fiber.Ctx) error { // Closes the connection instantly after writing from this handler // and disallow further modification of its response defer c.End() c.SendString("Hello, ... I forgot what comes next!") return errors.New("some error") }) ``` ### Format Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format from the supplied offers. A default handler can be provided by setting the `MediaType` to `"default"`. If no offers match and no default is provided, a 406 (Not Acceptable) response is sent. The Content-Type is automatically set when a handler is selected. :::info If the Accept header is **not** specified, the first handler with a real media type will be used (entries with the `"default"` media type are skipped, since `"default"` is not a valid Content-Type value). If only a `"default"` handler is supplied, it is called without setting the Content-Type. ::: ```go title="Signature" func (c fiber.Ctx) Format(handlers ...ResFmt) error ``` ```go title="Example" // Accept: application/json => {"command":"eat","subject":"fruit"} // Accept: text/plain => Eat Fruit! // Accept: application/xml => Not Acceptable app.Get("/no-default", func(c fiber.Ctx) error { return c.Format( fiber.ResFmt{"application/json", func(c fiber.Ctx) error { return c.JSON(fiber.Map{ "command": "eat", "subject": "fruit", }) }}, fiber.ResFmt{"text/plain", func(c fiber.Ctx) error { return c.SendString("Eat Fruit!") }}, ) }) // Accept: application/json => {"command":"eat","subject":"fruit"} // Accept: text/plain => Eat Fruit! // Accept: application/xml => Eat Fruit! app.Get("/default", func(c fiber.Ctx) error { textHandler := func(c fiber.Ctx) error { return c.SendString("Eat Fruit!") } handlers := []fiber.ResFmt{ {"application/json", func(c fiber.Ctx) error { return c.JSON(fiber.Map{ "command": "eat", "subject": "fruit", }) }}, {"text/plain", textHandler}, {"default", textHandler}, } return c.Format(handlers...) }) ``` ### JSON Converts any **interface** or **string** to JSON using the [encoding/json](https://pkg.go.dev/encoding/json) package. :::info JSON also sets the content header to the `ctype` parameter. If no `ctype` is passed in, the header is set to `application/json; charset=utf-8` by default. ::: ```go title="Signature" func (c fiber.Ctx) JSON(data any, ctype ...string) error ``` ```go title="Example" type SomeStruct struct { Name string Age uint8 } app.Get("/json", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", Age: 20, } return c.JSON(data) // => Content-Type: application/json; charset=utf-8 // => {"Name": "Grame", "Age": 20} return c.JSON(fiber.Map{ "name": "Grame", "age": 20, }) // => Content-Type: application/json; charset=utf-8 // => {"name": "Grame", "age": 20} return c.JSON(fiber.Map{ "type": "https://example.com/probs/out-of-credit", "title": "You do not have enough credit.", "status": 403, "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc", }, "application/problem+json") // => Content-Type: application/problem+json // => "{ // => "type": "https://example.com/probs/out-of-credit", // => "title": "You do not have enough credit.", // => "status": 403, // => "detail": "Your current balance is 30, but that costs 50.", // => "instance": "/account/12345/msgs/abc", // => }" }) ``` ### JSONP Sends a JSON response with JSONP support. This method is identical to [JSON](ctx.md#json), except that it opts-in to JSONP callback support. By default, the callback name is simply `callback`. Override this by passing a **named string** in the method. ```go title="Signature" func (c fiber.Ctx) JSONP(data any, callback ...string) error ``` ```go title="Example" type SomeStruct struct { Name string Age uint8 } app.Get("/", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", Age: 20, } return c.JSONP(data) // => callback({"Name": "Grame", "Age": 20}) return c.JSONP(data, "customFunc") // => customFunc({"Name": "Grame", "Age": 20}) }) ``` ### Links Joins the links followed by the property to populate the response’s [Link HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) field. Quotes and backslashes in the `rel` value are escaped so the emitted quoted-string stays grammar-valid per RFC 9110. ```go title="Signature" func (c fiber.Ctx) Links(link ...string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Links( "http://api.example.com/users?page=2", "next", "http://api.example.com/users?page=5", "last", ) // Link: ; rel="next", // ; rel="last" // ... }) ``` ### Location Sets the response [Location](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location) HTTP header to the specified path parameter. ```go title="Signature" func (c fiber.Ctx) Location(path string) ``` ```go title="Example" app.Post("/", func(c fiber.Ctx) error { c.Location("http://example.com") c.Location("/foo/bar") return nil }) ``` ### MsgPack > **Note:** Before using any MsgPack-related features, make sure to follow the [MsgPack setup instructions](../guide/advance-format.md#msgpack). A compact binary alternative to [JSON](#json) for efficient data transfer between micro-services or from server to client. MessagePack serializes faster and yields smaller payloads than plain JSON. Converts any **interface** or **string** to MsgPack using the [shamaton/msgpack](https://pkg.go.dev/github.com/shamaton/msgpack/v3) package. :::info MsgPack also sets the content header to the `ctype` parameter. If no `ctype` is passed in, the header is set to `application/vnd.msgpack`. ::: ```go title="Signature" func (c fiber.Ctx) MsgPack(data any, ctype ...string) error ``` ```go title="Example" type SomeStruct struct { Name string Age uint8 } app.Get("/msgpack", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", Age: 20, } return c.MsgPack(data) // => Content-Type: application/vnd.msgpack // => 82 A4 4E 61 6D 65 A5 47 72 61 6D 65 A3 41 67 65 14 return c.MsgPack(fiber.Map{ "name": "Grame", "age": 20, }) // => Content-Type: application/vnd.msgpack // => 82 A4 6E 61 6D 65 A5 47 72 61 6D 65 A3 61 67 65 14 return c.MsgPack(fiber.Map{ "type": "https://example.com/probs/out-of-credit", "title": "You do not have enough credit.", "status": 403, "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc", }, "application/problem+msgpack") }) // => Content-Type: application/problem+msgpack // 85 A4 74 79 70 65 D9 27 68 74 74 70 73 3A 2F 2F 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F 70 72 6F 62 73 2F 6F 75 74 2D 6F 66 2D 63 72 65 64 69 74 A5 74 69 74 6C 65 BE 59 6F 75 20 64 6F 20 6E 6F 74 20 68 61 76 65 20 65 6E 6F 75 67 68 20 63 72 65 64 69 74 2E A6 73 74 61 74 75 73 CD 01 93 A6 64 65 74 61 69 6C D9 2E 59 6F 75 72 20 63 75 72 72 65 6E 74 20 62 61 6C 61 6E 63 65 20 69 73 20 33 30 2C 20 62 75 74 20 74 68 61 74 20 63 6F 73 74 73 20 35 30 2E A8 69 6E 73 74 61 6E 63 65 B7 2F 61 63 63 6F 75 6E 74 2F 31 32 33 34 35 2F 6D 73 67 73 2F 61 62 63 ``` ### Render Renders a view with data and sends a `text/html` response. By default, `Render` uses the default [**Go Template engine**](https://pkg.go.dev/html/template/). If you want to use another view engine, please take a look at our [**Template middleware**](https://docs.gofiber.io/template). ```go title="Signature" func (c fiber.Ctx) Render(name string, bind any, layouts ...string) error ``` ### Send Sets the HTTP response body. ```go title="Signature" func (c fiber.Ctx) Send(body []byte) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.Send([]byte("Hello, World!")) // => "Hello, World!" }) ``` Fiber also provides `SendString` and `SendStream` methods for raw inputs. :::tip Use this if you **don't need** type assertion, recommended for **faster** performance. ::: ```go title="Signature" func (c fiber.Ctx) SendString(body string) error func (c fiber.Ctx) SendStream(stream io.Reader, size ...int) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") // => "Hello, World!" return c.SendStream(bytes.NewReader([]byte("Hello, World!"))) // => "Hello, World!" }) ``` ### SendEarlyHints Sends an informational `103 Early Hints` response with one or more [`Link` headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Link) before the final response. This allows the browser to start preloading resources while the server prepares the full response. :::caution This feature requires HTTP/2 or newer. Some legacy HTTP/1.1 clients may not support sendEarlyHints. Early Hints (`103` responses) are supported in HTTP/2 and newer. Older HTTP/1.1 clients may ignore these interim responses or misbehave when receiving them. See [Enabling HTTP/2](../guide/reverse-proxy#enabling-http2) for instructions on how to use a reverse proxy (e.g. Nginx or Traefik) to enable HTTP/2 support. ::: For requests that are not HTTP/1.1 (e.g. HTTP/1.0), no interim `103` response is sent — RFC 9110 forbids sending 1xx responses to HTTP/1.0 clients — but the `Link` headers are still included in the final response. :::caution Interim responses need Fiber's own server. When the app is mounted into `net/http` via the `adaptor` middleware, there is no client connection for interim responses: the `103` is silently skipped and the `Link` headers are still delivered on the final response. ::: ```go title="Signature" func (c fiber.Ctx) SendEarlyHints(hints []string) error ``` ```go title="Example" hints := []string{"; rel=preload; as=script"} app.Get("/early", func(c fiber.Ctx) error { if err := c.SendEarlyHints(hints); err != nil { return err } return c.SendString("done") }) ``` ### SendFile Transfers the file from the given path. Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) response HTTP header field based on the **file** extension or format. ```go title="Config" title="Config" // SendFile defines configuration options when to transfer file with SendFile. type SendFile struct { // FS is the file system to serve the static files from. // You can use interfaces compatible with fs.FS like embed.FS, os.DirFS etc. // // Optional. Default: nil FS fs.FS // When set to true, the server tries minimizing CPU usage by caching compressed files. // This works differently than the github.com/gofiber/compression middleware. // You have to set Content-Encoding header to compress the file. // Available compression methods are gzip, br, and zstd. // // Optional. Default: false Compress bool `json:"compress"` // When set to true, enables byte range requests. // // Optional. Default: false ByteRange bool `json:"byte_range"` // When set to true, enables direct download. // // Optional. Default: false Download bool `json:"download"` // Expiration duration for inactive file handlers. // Use a negative time.Duration to disable it. // // Optional. Default: 10 * time.Second CacheDuration time.Duration `json:"cache_duration"` // The value for the Cache-Control HTTP-header // that is set on the file response. MaxAge is defined in seconds. // // Optional. Default: 0 MaxAge int `json:"max_age"` } ``` ```go title="Signature" title="Signature" func (c fiber.Ctx) SendFile(file string, config ...SendFile) error ``` ```go title="Example" app.Get("/not-found", func(c fiber.Ctx) error { return c.SendFile("./public/404.html") // Disable compression return c.SendFile("./static/index.html", fiber.SendFile{ Compress: false, }) }) ``` :::info If the file contains a URL-specific character, you have to escape it before passing the file path into the `SendFile` function. ::: ```go title="Example" app.Get("/file-with-url-chars", func(c fiber.Ctx) error { return c.SendFile(url.PathEscape("hash_sign_#.txt")) }) ``` :::info You can set the `CacheDuration` config property to `-1` to disable caching. ::: ```go title="Example" app.Get("/file", func(c fiber.Ctx) error { return c.SendFile("style.css", fiber.SendFile{ CacheDuration: -1, }) }) ``` :::info You can use multiple `SendFile` calls with different configurations in a single route. Fiber creates different filesystem handlers per config. ::: ```go title="Example" app.Get("/file", func(c fiber.Ctx) error { switch c.Query("config") { case "filesystem": return c.SendFile("style.css", fiber.SendFile{ FS: os.DirFS(".") }) case "filesystem-compress": return c.SendFile("style.css", fiber.SendFile{ FS: os.DirFS("."), Compress: true, }) case "compress": return c.SendFile("style.css", fiber.SendFile{ Compress: true, }) default: return c.SendFile("style.css") } return nil }) ``` :::info For sending multiple files from an embedded file system, [this functionality](../middleware/static.md#serving-files-using-embedfs) can be used. ::: ### SendStatus Sets the status code and the correct status message in the body if the response body is **empty**. :::tip You can find all used status codes and messages [in the Fiber source code](https://github.com/gofiber/fiber/blob/dffab20bcdf4f3597d2c74633a7705a517d2c8c2/utils.go#L183-L244). ::: ```go title="Signature" func (c fiber.Ctx) SendStatus(status int) error ``` ```go title="Example" app.Get("/not-found", func(c fiber.Ctx) error { return c.SendStatus(415) // => 415 "Unsupported Media Type" c.SendString("Hello, World!") return c.SendStatus(415) // => 415 "Hello, World!" }) ``` ### SendStream Sets the response body to a stream of data and adds an optional body size. ```go title="Signature" func (c fiber.Ctx) SendStream(stream io.Reader, size ...int) error ``` :::info `SendStream` operates asynchronously. The handler returns immediately after setting up the stream, but the actual reading and sending of data happens **after** the handler completes. This is handled by the underlying `fasthttp` library. If the provided stream implements `io.Closer`, it will be automatically closed by `fasthttp` after the response is fully sent or if an error occurs. ::: :::caution When passing `fiber.Ctx` as a `context.Context` to libraries that spawn goroutines (e.g., for streaming operations), those goroutines may attempt to access the context after the handler returns. Since `fiber.Ctx` is recycled and released after the handler completes, this can cause issues. **Recommended approach**: Use `c.Context()` or `c.RequestCtx()` instead of passing `c` directly to such libraries. See the [Context Guide](../guide/context.md) for more details. ::: ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.SendStream(bytes.NewReader([]byte("Hello, World!"))) // => "Hello, World!" }) ``` ```go title="Example with file streaming" app.Get("/download", func(c fiber.Ctx) error { file, err := os.Open("large-file.zip") if err != nil { return err } // File will be automatically closed by fasthttp after streaming completes stat, err := file.Stat() if err != nil { file.Close() return err } return c.SendStream(file, int(stat.Size())) }) ``` ### SendStreamWriter Sets the response body stream writer. :::note The argument `streamWriter` represents a function that populates the response body using a buffered stream writer. ::: ```go title="Signature" func (c Ctx) SendStreamWriter(streamWriter func(*bufio.Writer)) error ``` ```go title="Example" app.Get("/", func (c fiber.Ctx) error { return c.SendStreamWriter(func(w *bufio.Writer) { fmt.Fprintf(w, "Hello, World!\n") }) // => "Hello, World!" }) ``` :::info To send data before `streamWriter` returns, you can call `w.Flush()` on the provided writer. Otherwise, the buffered stream flushes after `streamWriter` returns. ::: :::note `w.Flush()` will return an error if the client disconnects before `streamWriter` finishes writing a response. ::: ```go title="Example" app.Get("/wait", func(c fiber.Ctx) error { return c.SendStreamWriter(func(w *bufio.Writer) { // Begin Work fmt.Fprintf(w, "Please wait for 10 seconds\n") if err := w.Flush(); err != nil { log.Print("Client disconnected!") return } // Send progress over time time.Sleep(time.Second) for i := 0; i < 9; i++ { fmt.Fprintf(w, "Still waiting...\n") if err := w.Flush(); err != nil { // If client disconnected, cancel work and finish log.Print("Client disconnected!") return } time.Sleep(time.Second) } // Finish fmt.Fprintf(w, "Done!\n") }) }) ``` ### SendString Sets the response body to a string. ```go title="Signature" func (c fiber.Ctx) SendString(body string) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") // => "Hello, World!" }) ``` ### Set Sets the response’s HTTP header field to the specified `key`, `value`. ```go title="Signature" func (c fiber.Ctx) Set(key string, val string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Set("Content-Type", "text/plain") // => "Content-Type: text/plain" // ... }) ``` ### Status Sets the HTTP status for the response. :::info This method is **chainable**. ::: ```go title="Signature" func (c fiber.Ctx) Status(status int) fiber.Ctx ``` ```go title="Example" app.Get("/fiber", func(c fiber.Ctx) error { c.Status(fiber.StatusOK) return nil }) app.Get("/hello", func(c fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("Bad Request") }) app.Get("/world", func(c fiber.Ctx) error { return c.Status(fiber.StatusNotFound).SendFile("./public/gopher.png") }) ``` ### Type Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) HTTP header to the MIME type listed [in the Nginx MIME types configuration](https://github.com/nginx/nginx/blob/master/conf/mime.types) specified by the file **extension**. :::info This method is **chainable**. ::: ```go title="Signature" func (c fiber.Ctx) Type(ext string, charset ...string) fiber.Ctx ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Type(".html") // => "text/html" c.Type("html") // => "text/html" c.Type("png") // => "image/png" c.Type("json", "utf-8") // => "application/json; charset=utf-8" // ... }) ``` ### Vary Adds the given header field to the [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) response header. This will append the header if not already listed; otherwise, it leaves it listed in the current location. :::info Multiple fields are **allowed**. Per RFC 9110, the wildcard `"*"` is only meaningful as the sole member of the field: adding `"*"` collapses the header to a single `*`, and once `*` is present no further fields are appended. ::: ```go title="Signature" func (c fiber.Ctx) Vary(fields ...string) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Vary("Origin") // => Vary: Origin c.Vary("User-Agent") // => Vary: Origin, User-Agent // No duplicates c.Vary("Origin") // => Vary: Origin, User-Agent c.Vary("Accept-Encoding", "Accept") // => Vary: Origin, User-Agent, Accept-Encoding, Accept c.Vary("*") // => Vary: * // ... }) ``` ### Write Adopts the `Writer` interface. ```go title="Signature" func (c fiber.Ctx) Write(p []byte) (n int, err error) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { c.Write([]byte("Hello, World!")) // => "Hello, World!" fmt.Fprintf(c, "%s\n", "Hello, World!") // => "Hello, World!" }) ``` ### Writef Writes a formatted string using a format specifier. ```go title="Signature" func (c fiber.Ctx) Writef(format string, a ...any) (n int, err error) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { world := "World!" c.Writef("Hello, %s", world) // => "Hello, World!" fmt.Fprintf(c, "%s\n", "Hello, World!") // => "Hello, World!" }) ``` ### WriteString Writes a string to the response body. ```go title="Signature" func (c fiber.Ctx) WriteString(s string) (n int, err error) ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.WriteString("Hello, World!") // => "Hello, World!" }) ``` ### XML Converts any **interface** or **string** to XML using the standard `encoding/xml` package. :::info XML also sets the content header to `application/xml; charset=utf-8`. ::: ```go title="Signature" func (c fiber.Ctx) XML(data any) error ``` ```go title="Example" type SomeStruct struct { XMLName xml.Name `xml:"Fiber"` Name string `xml:"Name"` Age uint8 `xml:"Age"` } app.Get("/", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", Age: 20, } return c.XML(data) // // Grame // 20 // }) ``` --- ## 📦 Fiber ## Server start ### New This method creates a new **App** named instance. You can pass optional [config](#config) when creating a new instance. ```go title="Signature" func New(config ...Config) *App ``` ```go title="Example" // Default config app := fiber.New() // ... ``` ### Config You can pass an optional Config when creating a new Fiber instance. ```go title="Example" // Custom config app := fiber.New(fiber.Config{ CaseSensitive: true, StrictRouting: true, ServerHeader: "Fiber", AppName: "Test App v1.0.1", }) // ... ``` #### Config fields | Property | Type | Description | Default | |---------------------------------------------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------| | AppName | `string` | Sets the application name used in logs and the Server header | `""` | | BodyLimit | `int` | Sets the maximum allowed size for a request body. Zero or negative values fall back to the default limit. If the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. This limit also applies when running Fiber through the adaptor middleware from `net/http`, when decoding compressed request bodies via [`Ctx.Body()`](./ctx.md#body), and when parsing multipart form data via [`Ctx.MultipartForm()`](./ctx.md#multipartform). | `4 * 1024 * 1024` | | CaseSensitive | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo` and `/foo` are treated the same. | `false` | | CBORDecoder | `utils.CBORUnmarshal` | Allowing for flexibility in using another cbor library for decoding. | `binder.UnimplementedCborUnmarshal` | | CBOREncoder | `utils.CBORMarshal` | Allowing for flexibility in using another cbor library for encoding. | `binder.UnimplementedCborMarshal` | | ColorScheme | [`Colors`](https://github.com/gofiber/fiber/blob/main/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/main/color.go) | | CompressedFileSuffixes | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` | | Concurrency | `int` | Maximum number of concurrent connections. | `256 * 1024` | | DisableDefaultContentType | `bool` | When true, omits the default Content-Type header from the response. | `false` | | DisableDefaultDate | `bool` | When true, omits the Date header from the response. | `false` | | DisableHeadAutoRegister | `bool` | Prevents Fiber from automatically registering `HEAD` routes for each `GET` route so you can supply custom `HEAD` handlers; manual `HEAD` routes still override the generated ones. | `false` | | DisableHeaderNormalizing | `bool` | By default all header names are normalized: conteNT-tYPE -> Content-Type | `false` | | DisableKeepalive | `bool` | Disables keep-alive connections so the server closes each connection after the first response. | `false` | | DisablePreParseMultipartForm | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` | | EnableIPValidation | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma separated string.**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` | | EnableSplittingOnParsers | `bool` | Splits query, body, and header parameters on commas when enabled.For example, `/api?foo=bar,baz` becomes `foo[]=bar&foo[]=baz`. | `false` | | ErrorHandler | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` | | GETOnly | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` | | IdleTimeout | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `0` | | Immutable | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` | | JSONDecoder | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` | | JSONEncoder | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` | | MaxRanges | `int` | Sets the maximum number of ranges parsed from a `Range` header. Zero or negative values fall back to the default limit. If the limit is exceeded, the request is rejected with `416 - Requested Range Not Satisfiable` and `Content-Range: bytes */`. | `16` | | MsgPackDecoder | `utils.MsgPackUnmarshal` | Allowing for flexibility in using another msgpack library for decoding. | `binder.UnimplementedMsgpackUnmarshal` | | MsgPackEncoder | `utils.MsgPackMarshal` | Allowing for flexibility in using another msgpack library for encoding. | `binder.UnimplementedMsgpackMarshal` | | PassLocalsToContext | `bool` | Controls whether `StoreInContext` also propagates values into the request `context.Context` for Fiber-backed contexts. `StoreInContext` always writes to `c.Locals()`. `ValueFromContext` for Fiber-backed contexts always reads from `c.Locals()`. | `false` | | PassLocalsToViews | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` | | ProxyHeader | `string` | Specifies the header name to read the client's real IP address from when behind a reverse proxy. Common values: `fiber.HeaderXForwardedFor`, `"X-Real-IP"`, `"CF-Connecting-IP"` (Cloudflare). **Important:** This setting **requires** `TrustProxy` to be enabled; `TrustProxyConfig` controls which proxy IPs are trusted for reading this header. Without `TrustProxy`, this setting has no effect and `c.IP()` will always return the remote IP from the TCP connection. **Behavior note:** `X-Forwarded-For` often contains a comma-separated chain of IP addresses. With the default `EnableIPValidation = false`, `c.IP()` will return the raw header value (the whole chain) rather than a single parsed client IP. With `EnableIPValidation = true`, `c.IP()` parses the header and returns the **first syntactically valid IP address** it finds; it does **not** walk the chain to find the first non-proxy hop. For a reliable client IP, configure your reverse proxy to overwrite or sanitize this header and/or to provide a single-IP header such as `"X-Real-IP"` or a provider-specific header like `"CF-Connecting-IP"`. **Security Warning:** Headers can be easily spoofed. Always configure `TrustProxyConfig` to validate the proxy IP address, otherwise malicious clients can forge headers to bypass IP-based access controls. | `""` | | ReadBufferSize | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` | | ReadTimeout | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `0` | | ReduceMemoryUsage | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` | | RegexHandler | `any` | Configures the compiler used for `regex()` route constraints. Assign `regexp.MustCompile` or `coregex.MustCompile` directly to switch engines. Fiber reuses the compiled matcher across requests, so the returned value must be safe for concurrent use. Fiber may invoke `RegexHandler` more than once per route while parsing raw and normalized route patterns during registration. | `regexp.MustCompile` | | RequestMethods | `[]string` | RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` | | ServerHeader | `string` | Enables the `Server` HTTP header with the given value. | `""` | | SkipUnmatchedRoutes | `bool` | When enabled, requests whose path and method match no registered route are answered with `404` (or `405` when the path exists for other methods) before the middleware chain runs, avoiding work on requests to unregistered paths (bots, scanners, bad URLs). Warning: middleware never runs for skipped requests, so Use-based responders on unregistered paths (catch-all 404 pages, static, proxy, healthcheck, rewrite/redirect middleware) and logger/metrics visibility stop working for them; CORS preflight requests are exempt so cors middleware keeps working. Customize the responses via `ErrorHandler`. | `false` | | StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` | | StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` | | StructValidator | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` | | TrustProxy | `bool` | Enables trust of reverse proxy headers. When enabled, Fiber will check if the request is coming from a trusted proxy (configured in `TrustProxyConfig`) before reading values from proxy headers. **Required for**: Using `ProxyHeader` to read client IP from headers like `X-Forwarded-For`. **Behavior when enabled:** If the remote IP is trusted (matches `TrustProxyConfig`), then `c.IP()` reads from `ProxyHeader` (when configured; otherwise it uses `RemoteIP()`), `c.Scheme()` first checks standard proxy scheme headers (`X-Forwarded-Proto`, `X-Forwarded-Protocol`, `X-Forwarded-Ssl`, `X-Url-Scheme`) and falls back to the actual connection scheme if none are set, and `c.Hostname()` prefers `X-Forwarded-Host` but falls back to the request Host header when the proxy header is not present. If the remote IP is NOT trusted, these methods ignore proxy headers and use the actual connection values instead. **Security:** This prevents header spoofing by validating the proxy's IP address. Always configure `TrustProxyConfig` when enabling this option and set `ProxyHeader` if you want `c.IP()` to use a specific header. | `false` | | TrustProxyConfig | `TrustProxyConfig` | Configures which proxy IP addresses or ranges to trust. Only effective when `TrustProxy` is enabled. **Fields:** • `Proxies` - List of trusted proxy IPs or CIDR ranges (e.g., `[]string{"10.10.0.58", "192.168.0.0/24"}`) • `Loopback` - Trust loopback addresses (127.0.0.0/8, ::1/128) • `Private` - Trust all private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7) • `LinkLocal` - Trust link-local addresses (169.254.0.0/16, fe80::/10) • `UnixSocket` - Trust Unix domain socket connections **Example:** For an app behind Nginx at 10.10.0.58, use `TrustProxyConfig{Proxies: []string{"10.10.0.58"}}` or `TrustProxyConfig{Private: true}` if using private network IPs. | `{}` | | UnescapePath | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` | | Views | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` | | ViewsLayout | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` | | WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | `4096` | | WriteTimeout | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `0` | | XMLDecoder | `utils.XMLUnmarshal` | Allowing for flexibility in using another XML library for decoding. | `xml.Unmarshal` | | XMLEncoder | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` | ## Server listening ### Config You can pass an optional ListenConfig when calling the [`Listen`](#listen) or [`Listener`](#listener) method. ```go title="Example" // Custom config app.Listen(":8080", fiber.ListenConfig{ EnablePrefork: true, DisableStartupMessage: true, }) ``` #### Config fields | Property | Type | Description | Default | |-------------------------------------------------------------------------|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------| | BeforeServeFunc | `func(app *App) error` | Allows customizing and accessing fiber app before serving the app. | `nil` | | CertClientFile | `string` | Path of the client certificate. If you want to use mTLS, you must enter this field. | `""` | | CertFile | `string` | Path of the certificate file. If you want to use TLS, you must enter this field. | `""` | | CertKeyFile | `string` | Path of the certificate's private key. If you want to use TLS, you must enter this field. | `""` | | DisableStartupMessage | `bool` | When set to true, it will not print out the «Fiber» ASCII art and listening address. | `false` | | EnablePrefork | `bool` | When set to true, this will spawn multiple Go processes listening on the same port. | `false` | | EnablePrintRoutes | `bool` | If set to true, will print all routes with their method, path, and handler. | `false` | | GracefulContext | `context.Context` | Field to shutdown Fiber by given context gracefully. | `nil` | | ShutdownTimeout | `time.Duration` | Specifies the maximum duration to wait for the server to gracefully shutdown. When the timeout is reached, the graceful shutdown process is interrupted and forcibly terminated, and the `context.DeadlineExceeded` error is passed to the `OnPostShutdown` callback. Set to 0 to disable the timeout and wait indefinitely. | `10 * time.Second` | | ListenerAddrFunc | `func(addr net.Addr)` | Allows accessing and customizing `net.Listener`. | `nil` | | ListenerNetwork | `string` | Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "unix" (Unix Domain Sockets). WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `tcp4` | | PreforkRecoverInterval | `time.Duration` | Delays the respawn of a crashed child process by this duration. Only applies when prefork is enabled. | `0` (respawn immediately) | | PreforkRecoverThreshold | `int` | Defines the maximum number of child process restarts after crashes before the prefork master exits with an error. Only applies when prefork is enabled. | `max(1, runtime.GOMAXPROCS(0) / 2)` | | PreforkShutdownGracePeriod | `time.Duration` | How long the prefork master waits for child processes to exit after SIGTERM before sending SIGKILL during shutdown. On Windows children are always killed immediately. Only applies when prefork is enabled. | `5 * time.Second` | | PreforkLogger | `PreforkLogger` | Sets a custom logger for the prefork process manager. Only applies when prefork is enabled. | Fiber logger | | UnixSocketFileMode | `os.FileMode` | FileMode to set for Unix Domain Socket (ListenerNetwork must be "unix") | `0770` | | TLSConfigFunc | `func(tlsConfig *tls.Config)` | Allows customizing `tls.Config` as you want. Ignored when `TLSConfig` is set. | `nil` | | TLSConfig | `*tls.Config` | Recommended base TLS configuration (cloned). Use for external certificate providers via `GetCertificate`. When set, other TLS fields are ignored. | `nil` | | AutoCertManager | `*autocert.Manager` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | | TLSMinVersion | `uint16` | Allows customizing the TLS minimum version. | `tls.VersionTLS12` | ### Listen Listen serves HTTP requests from the given address. ```go title="Signature" func (app *App) Listen(addr string, config ...ListenConfig) error ``` ```go title="Basic Listen usage" // Listen on port :8080 app.Listen(":8080") // Listen on port :8080 with Prefork app.Listen(":8080", fiber.ListenConfig{EnablePrefork: true}) // Custom host app.Listen("127.0.0.1:8080") ``` #### Prefork Prefork is a feature that allows you to spawn multiple Go processes listening on the same port. This can be useful for scaling across multiple CPU cores. ```go title="Prefork listener" app.Listen(":8080", fiber.ListenConfig{EnablePrefork: true}) ``` Depending on the operating system, prefork can distribute incoming connections between the spawned processes and allow more requests to be handled simultaneously. On Linux, prefork typically relies on the `SO_REUSEPORT` socket option for kernel-assisted load distribution across workers. On Windows, Fiber falls back to `SO_REUSEADDR`; this is not a functional equivalent to Linux `SO_REUSEPORT` as it lacks native load balancing and may allow other processes to bind to the same port. Operators should validate this behavior against their security and availability requirements. ##### Security Considerations Prefork changes the port-ownership model from strict single-owner binding to an intentional multi-listener setup. In shared hosts, a local co-resident attacker with sufficient privileges may be able to race for shared binds or receive a portion of traffic, depending on platform behavior and user boundaries. - Run prefork only within a trusted boundary (same deployment unit / same trust domain). - Use a dedicated service account for Fiber workers; avoid broad shared-user deployments. - Prefer container or VM isolation and avoid shared host namespaces for unrelated workloads. - If strict single-owner port semantics are required, run Fiber without prefork. #### TLS Prefer `TLSConfig` for TLS configuration so you can fully control certificates and settings. When `TLSConfig` is set, Fiber ignores `CertFile`, `CertKeyFile`, `CertClientFile`, `TLSMinVersion`, `AutoCertManager`, and `TLSConfigFunc`. TLS serves HTTPs requests from the given address using certFile and keyFile paths as TLS certificate and key file. ```go title="TLS with cert and key files" app.Listen(":443", fiber.ListenConfig{CertFile: "./cert.pem", CertKeyFile: "./cert.key"}) ``` #### TLS with client CA certificate `CertClientFile` only configures the client CA for mTLS when using `CertFile`/`CertKeyFile`. If `TLSConfig` is set, `CertClientFile` is ignored, so configure client CAs in the provided `tls.Config` instead. ```go title="TLS with client CA certificate" app.Listen(":443", fiber.ListenConfig{ CertFile: "./cert.pem", CertKeyFile: "./cert.key", CertClientFile: "./ca-chain-cert.pem", }) ``` #### TLS AutoCert support (ACME / Let's Encrypt) Provides automatic access to certificates management from Let's Encrypt and any other ACME-based providers. ```go title="AutoCert (ACME) configuration" // Certificate manager certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, // Replace with your domain name HostPolicy: autocert.HostWhitelist("example.com"), // Folder to store the certificates Cache: autocert.DirCache("./certs"), } app.Listen(":444", fiber.ListenConfig{ AutoCertManager: certManager, }) ``` #### Precedence and conflicts - `TLSConfig` is preferred and ignores `CertFile`/`CertKeyFile`, `CertClientFile`, `AutoCertManager`, `TLSMinVersion`, and `TLSConfigFunc`. - `AutoCertManager` cannot be combined with `CertFile`/`CertKeyFile`. #### TLS with external certificate provider Use `TLSConfig` to supply a base `tls.Config` that can fetch certificates at runtime. `TLSConfig` is cloned and used as-is. ```go title="TLSConfig with dynamic certificate provider" app.Listen(":443", fiber.ListenConfig{ TLSConfig: &tls.Config{ GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { return myProvider.Certificate(info.ServerName) }, }, }) ``` #### Mutual TLS with TLSConfig Use `TLSConfig` to configure mutual TLS by setting `ClientAuth` and `ClientCAs`. This replaces `CertClientFile` when you manage TLS configuration directly. ```go title="TLSConfig with client CA pool" certPEM := []byte(certPEMString) keyPEM := []byte(keyPEMString) caPEM := []byte(caPEMString) cert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { log.Fatal(err) } clientCAs := x509.NewCertPool() if ok := clientCAs.AppendCertsFromPEM(caPEM); !ok { log.Fatal("failed to append client CA") } app.Listen(":443", fiber.ListenConfig{ TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }, }) ``` Load certificates from memory or environment variables and provide them via `TLSConfig`. ```go title="TLSConfig with in-memory certificate" certPEM := []byte(certPEMString) keyPEM := []byte(keyPEMString) cert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { log.Fatal(err) } app.Listen(":443", fiber.ListenConfig{ TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }, }) ``` ```go title="TLSConfig with certificate from environment" certPEM := []byte(os.Getenv("TLS_CERT_PEM")) keyPEM := []byte(os.Getenv("TLS_KEY_PEM")) cert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { log.Fatal(err) } app.Listen(":443", fiber.ListenConfig{ TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }, }) ``` ### Listener You can pass your own [`net.Listener`](https://pkg.go.dev/net/#Listener) using the `Listener` method. This method can be used to enable **TLS/HTTPS** with a custom tls.Config. ```go title="Signature" func (app *App) Listener(ln net.Listener, config ...ListenConfig) error ``` ```go title="Examples" ln, _ := net.Listen("tcp", ":3000") cer, _:= tls.LoadX509KeyPair("server.crt", "server.key") ln = tls.NewListener(ln, &tls.Config{Certificates: []tls.Certificate{cer}}) app.Listener(ln) ``` ## Server Server returns the underlying [fasthttp server](https://godoc.org/github.com/valyala/fasthttp#Server) ```go title="Signature" func (app *App) Server() *fasthttp.Server ``` ```go title="Examples" func main() { app := fiber.New() app.Server().MaxConnsPerIP = 1 // ... } ``` ## Server Shutdown Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners and then waits indefinitely for all connections to return to idle before shutting down. ShutdownWithTimeout will forcefully close any active connections after the timeout expires. ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded. Shutdown hooks will still be executed, even if an error occurs during the shutdown process, as they are deferred to ensure cleanup happens regardless of errors. ```go func (app *App) Shutdown() error func (app *App) ShutdownWithTimeout(timeout time.Duration) error func (app *App) ShutdownWithContext(ctx context.Context) error ``` ## Helper functions ### NewError NewError creates a new HTTPError instance with an optional message. ```go title="Signature" func NewError(code int, message ...string) *Error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return fiber.NewError(782, "Custom error message") }) ``` ### NewErrorf NewErrorf creates a new HTTPError instance with an optional formatted message. ```go title="Signature" func NewErrorf(code int, message ...any) *Error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return fiber.NewErrorf(782, "Custom error %s", "message") }) ``` ### IsChild IsChild determines if the current process is a result of Prefork. ```go title="Signature" func IsChild() bool ``` ```go title="Example" // Config app app := fiber.New() app.Get("/", func(c fiber.Ctx) error { if !fiber.IsChild() { fmt.Println("I'm the parent process") } else { fmt.Println("I'm a child process") } return c.SendString("Hello, World!") }) // ... // With prefork enabled, the parent process will spawn child processes app.Listen(":8080", fiber.ListenConfig{EnablePrefork: true}) ``` --- ## 🎣 Hooks Fiber lets you run custom callbacks at specific points in the routing lifecycle. Available hooks include: - [OnRoute](#onroute) - [OnName](#onname) - [OnGroup](#ongroup) - [OnGroupName](#ongroupname) - [OnListen](#onlisten) - [OnPreStartupMessage/OnPostStartupMessage](#onprestartupmessageonpoststartupmessage) - [ListenData](#listendata) - [OnFork](#onfork) - [OnPreShutdown](#onpreshutdown) - [OnPostShutdown](#onpostshutdown) - [OnMount](#onmount) ## Constants ```go // Handlers define functions to create hooks for Fiber. type OnRouteHandler = func(Route) error type OnNameHandler = OnRouteHandler type OnGroupHandler = func(Group) error type OnGroupNameHandler = OnGroupHandler type OnListenHandler = func(ListenData) error type OnForkHandler = func(int) error type OnPreStartupMessageHandler = func(*PreStartupMessageData) error type OnPostStartupMessageHandler = func(*PostStartupMessageData) error type OnPreShutdownHandler = func() error type OnPostShutdownHandler = func(error) error type OnMountHandler = func(*App) error ``` ## OnRoute Runs after each route is registered. The callback receives the route so you can inspect its properties. ```go title="Signature" func (h *Hooks) OnRoute(handler ...OnRouteHandler) ``` ## OnName Runs when a route is named. The callback receives the route. :::caution `OnName` only works with named routes, not groups. ::: ```go title="Signature" func (h *Hooks) OnName(handler ...OnNameHandler) ``` ```go package main import ( "fmt" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("index") app.Hooks().OnName(func(r fiber.Route) error { fmt.Print("Name: " + r.Name + ", ") return nil }) app.Hooks().OnName(func(r fiber.Route) error { fmt.Print("Method: " + r.Method + "\n") return nil }) app.Get("/add/user", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("addUser") app.Delete("/destroy/user", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("destroyUser") app.Listen(":5000") } // Results: // Name: addUser, Method: GET // Name: destroyUser, Method: DELETE ``` ## OnGroup Runs after each group is registered. The callback receives the group. ```go title="Signature" func (h *Hooks) OnGroup(handler ...OnGroupHandler) ``` ## OnGroupName Runs when a group is named. The callback receives the group. :::caution `OnGroupName` only works with named groups, not routes. ::: ```go title="Signature" func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) ``` ## OnListen Runs when the app starts listening via `Listen` or `Listener`. ```go title="Signature" func (h *Hooks) OnListen(handler ...OnListenHandler) ``` ```go package main import ( "log" "os" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" ) func main() { app := fiber.New(fiber.Config{ DisableStartupMessage: true, }) app.Hooks().OnListen(func(listenData fiber.ListenData) error { if fiber.IsChild() { return nil } scheme := "http" if listenData.TLS { scheme = "https" } log.Println(scheme + "://" + listenData.Host + ":" + listenData.Port) return nil }) app.Listen(":5000") } ``` ## OnPreStartupMessage/OnPostStartupMessage Use `OnPreStartupMessage` to tweak the banner before Fiber prints it, and `OnPostStartupMessage` to run logic after the banner is printed (or skipped). You can use some helper functions to customize the banner inside the `OnPreStartupMessage` hook. ```go title="Signatures" // AddInfo adds an informational entry to the startup message with "INFO" label. func (sm *PreStartupMessageData) AddInfo(key, title, value string, priority ...int) // AddWarning adds a warning entry to the startup message with "WARNING" label. func (sm *PreStartupMessageData) AddWarning(key, title, value string, priority ...int) // AddError adds an error entry to the startup message with "ERROR" label. func (sm *PreStartupMessageData) AddError(key, title, value string, priority ...int) // EntryKeys returns all entry keys currently present in the startup message. func (sm *PreStartupMessageData) EntryKeys() []string // ResetEntries removes all existing entries from the startup message. func (sm *PreStartupMessageData) ResetEntries() // DeleteEntry removes a specific entry from the startup message by its key. func (sm *PreStartupMessageData) DeleteEntry(key string) ``` - Assign `sm.BannerHeader` to override the ASCII art banner. Leave it empty to use the default banner provided by Fiber. - Set `sm.PreventDefault = true` to suppress the built-in banner without affecting other hooks. - `PostStartupMessageData` reports whether the banner was skipped via the `Disabled`, `IsChild`, and `Prevented` flags. ### Startup Message Customization ```go title="Customize the startup message" package main import ( "fmt" "os" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { sm.BannerHeader = "FOOBER " + sm.Version + "\n-------" // Optional: you can also remove old entries // sm.ResetEntries() sm.AddInfo("git-hash", "Git hash", os.Getenv("GIT_HASH")) sm.AddInfo("prefork", "Prefork", fmt.Sprintf("%v", sm.Prefork), 15) return nil }) app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error { if !sm.Disabled && !sm.IsChild && !sm.Prevented { fmt.Println("startup completed") } return nil }) app.Listen(":5000") } ``` ### ListenData `ListenData` exposes runtime metadata about the listener: | Field | Type | Description | | --- | --- | --- | | `Host` | `string` | Resolved hostname or IP address. | | `Port` | `string` | The bound port. | | `TLS` | `bool` | Indicates whether TLS is enabled. | | `Version` | `string` | Fiber version reported in the startup banner. | | `AppName` | `string` | Application name from the configuration. | | `HandlerCount` | `int` | Total registered handler count. | | `ProcessCount` | `int` | Number of processes Fiber will use. | | `PID` | `int` | Current process identifier. | | `Prefork` | `bool` | Whether prefork is enabled. | | `ChildPIDs` | `[]int` | Child process identifiers when preforking. | | `ColorScheme` | [`Colors`](https://github.com/gofiber/fiber/blob/main/color.go) | Active color scheme for the startup message. | ## OnFork Runs in the child process after a fork. ```go title="Signature" func (h *Hooks) OnFork(handler ...OnForkHandler) ``` ## OnPreShutdown Runs before the server shuts down. ```go title="Signature" func (h *Hooks) OnPreShutdown(handler ...OnPreShutdownHandler) ``` ## OnPostShutdown Runs after the server shuts down. ```go title="Signature" func (h *Hooks) OnPostShutdown(handler ...OnPostShutdownHandler) ``` ## OnMount Fires after a sub-app is mounted on a parent. The parent app is passed to the callback and it works for both app and group mounts. ```go title="Signature" func (h *Hooks) OnMount(handler ...OnMountHandler) ``` ```go package main import ( "fmt" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", testSimpleHandler).Name("x") subApp := fiber.New() subApp.Get("/test", testSimpleHandler) subApp.Hooks().OnMount(func(parent *fiber.App) error { fmt.Print("Mount path of parent app: " + parent.MountPath()) // Additional custom logic... return nil }) app.Use("/sub", subApp) } func testSimpleHandler(c fiber.Ctx) error { return c.SendString("Hello, Fiber!") } // Result: // Mount path of parent app: /sub ``` :::caution OnName, OnRoute, OnGroup, and OnGroupName are mount-sensitive. When you mount a sub-app that registers these hooks, route and group paths include the mount prefix. ::: --- ## 📃 Log Logs help you observe program behavior, diagnose issues, and trigger alerts. Structured logs improve searchability and speed up troubleshooting. Fiber logs to standard output by default and exposes global helpers such as `log.Info`, `log.Errorf`, and `log.Warnw`. ## Log Levels ```go const ( LevelTrace Level = iota LevelDebug LevelInfo LevelWarn LevelError LevelFatal LevelPanic ) ``` ## Custom Log Fiber provides the generic `AllLogger[T]` interface for adapting various log libraries. ```go type CommonLogger interface { Logger FormatLogger WithLogger } type ConfigurableLogger[T any] interface { // SetLevel sets logging level. SetLevel(level Level) // SetOutput sets the logger output. SetOutput(w io.Writer) // Logger returns the logger instance. Logger() T } type AllLogger[T any] interface { CommonLogger ConfigurableLogger[T] // WithContext returns a new logger with the given context. WithContext(ctx any) CommonLogger } ``` ## Print Log **Note:** The Fatal level method will terminate the program after printing the log message. Please use it with caution. ### Basic Logging Call level-specific methods directly; entries use the `messageKey` (default `msg`). ```go log.Info("Hello, World!") log.Debug("Are you OK?") log.Info("42 is the answer to life, the universe, and everything") log.Warn("We are under attack!") log.Error("Houston, we have a problem.") log.Fatal("So Long, and Thanks for All the Fish.") log.Panic("The system is down.") ``` ### Formatted Logging Append `f` to format the message. ```go log.Debugf("Hello %s", "boy") log.Infof("%d is the answer to life, the universe, and everything", 42) log.Warnf("We are under attack, %s!", "boss") log.Errorf("%s, we have a problem.", "John Smith") log.Fatalf("So Long, and Thanks for All the %s.", "fish") ``` ### Key-Value Logging Key-value helpers log structured fields; mismatched pairs emit `KEYVALS UNPAIRED`. ```go log.Debugw("", "greeting", "Hello", "target", "boy") log.Infow("", "number", 42) log.Warnw("", "job", "boss") log.Errorw("", "name", "John Smith") log.Fatalw("", "fruit", "fish") ``` ## Global Log Fiber also exposes a global logger for quick messages. ```go import "github.com/gofiber/fiber/v3/log" log.Info("info") log.Warn("warn") ``` The example uses `log.DefaultLogger`, which writes to stdout. The [contrib](https://github.com/gofiber/contrib) repo offers adapters like `fiberzap` and `fiberzerolog`, or you can register your own with `log.SetLogger`. Here's an example using a custom logger: ```go import ( "log" fiberlog "github.com/gofiber/fiber/v3/log" ) var _ fiberlog.AllLogger[*log.Logger] = (*customLogger)(nil) type customLogger struct { stdlog *log.Logger } // Implement required methods for the AllLogger interface... // Inject your custom logger fiberlog.SetLogger[*log.Logger](&customLogger{ stdlog: log.New(os.Stdout, "CUSTOM ", log.LstdFlags), }) // Retrieve the underlying *log.Logger for direct use std := fiberlog.DefaultLogger[*log.Logger]().Logger() std.Println("custom logging") ``` ## Set Level `log.SetLevel` sets the minimum level that will be output. The default is `LevelTrace`. **Note:** This method is not concurrent safe. ```go import "github.com/gofiber/fiber/v3/log" log.SetLevel(log.LevelInfo) ``` Setting the log level allows you to control the verbosity of the logs, filtering out messages below the specified level. ## Set Output `log.SetOutput` sets where logs are written. By default, they go to the console. ### Writing Logs to Stderr `log.SetOutput(os.Stderr)` redirects the default logger to standard error. ```go import ( "os" fiberlog "github.com/gofiber/fiber/v3/log" ) fiberlog.SetOutput(os.Stderr) ``` This lets you route logs to a file, service, or any destination. ### Writing Logs to a File To write to a file such as `test.log`: ```go // Output to ./test.log file f, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { log.Fatal("Failed to open log file:", err) } log.SetOutput(f) ``` ### Writing Logs to Both Console and File Write to both `test.log` and `stdout`: ```go // Output to ./test.log file file, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { log.Fatal("Failed to open log file:", err) } iw := io.MultiWriter(os.Stdout, file) log.SetOutput(iw) ``` ## Bind Context Bind a logger to a context with `log.WithContext`, which returns a `CommonLogger` tied to that context. ```go commonLogger := log.WithContext(ctx) commonLogger.Info("info") ``` Context binding can render request-specific data for easier tracing. The default context format is `log.DefaultFormat` (the empty string), so `log.WithContext(ctx)` adds no fields until you configure a format with `log.SetContextTemplate` (or its `MustSetContextTemplate` panic-on-error variant). `SetContextTemplate` configures Fiber's built-in default logger. Custom loggers registered with `SetLogger` keep full control over their own `WithContext` behavior and should implement equivalent enrichment themselves when needed. ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" "github.com/gofiber/fiber/v3/middleware/requestid" ) app.Use(requestid.New()) log.MustSetContextTemplate(log.ContextConfig{Format: log.RequestIDFormat}) app.Get("/", func(c fiber.Ctx) error { log.WithContext(c).Info("start") return c.SendString("Hello, World!") }) ``` Middleware that stores request values registers its log context tags automatically. For example, importing `requestid` makes `${requestid}` and `${request-id}` available; the actual value is filled in once `requestid.New()` runs in your handler stack. Until then the tag renders as an empty string, so a format that references `${requestid}` still compiles without `requestid.New()` in the chain. Use `log.WithContext(c)` inside handlers when you want tags to read values stored by Fiber middleware. Passing `c.Context()` only exposes values propagated into the standard request context. :::tip Cross-reference: the access-log integration uses the same tag names — see [middleware/logger](../middleware/logger.md#config) for the request-time format. ::: ### Glossary - **Format** — the string with `${...}` placeholders, e.g. `"[${requestid}] "`. - **Template** — the compiled, reusable form of a format produced by `SetContextTemplate`. - **Tag** — a single `${name}` (bare) or `${name:param}` (parametric) placeholder inside a format. ### Signatures ```go // Configure the active context template (or pass log.ContextConfig{} to disable). func SetContextTemplate(config ContextConfig) error func MustSetContextTemplate(config ContextConfig) // Register a tag globally; the new renderer becomes available to subsequent // SetContextTemplate calls. The reserved TagContextValue ("value:") name // cannot be registered. func RegisterContextTag(tag string, fn ContextTagFunc) error func MustRegisterContextTag(tag string, fn ContextTagFunc) // Bind a context to the default logger. ctx accepts fiber.Ctx, // *fasthttp.RequestCtx, context.Context, or any value exposing // Value(key any)/UserValue(key any). func WithContext(ctx any) CommonLogger // Public types referenced by the API above. type ContextConfig struct { CustomTags map[string]ContextTagFunc Format string } type ContextData struct{} type ContextTagFunc = logtemplate.Func[any, ContextData] type Buffer = logtemplate.Buffer // Format constants. const ( DefaultFormat = "" RequestIDFormat = "[${requestid}] " KeyValueFormat = "request-id=${request-id} username=${username} api-key=${api-key} csrf-token=${csrf-token} session-id=${session-id} " TagContextValue = "value:" ) ``` ### Context Formats | Format Constant | Format String | Description | | :-- | :-- | :-- | | `DefaultFormat` | `""` | Disables contextual fields. | | `RequestIDFormat` | `"[${requestid}] "` | Prepends the request ID when the requestid middleware is used. | | `KeyValueFormat` | `"request-id=${request-id} username=${username} api-key=${api-key} csrf-token=${csrf-token} session-id=${session-id} "` | Prepends common middleware context values as key/value fields. Sensitive values are redacted by the registering middleware. | ### Context Tags | Tag | Source | | :-- | :-- | | `${requestid}` / `${request-id}` | `requestid` middleware | | `${username}` | `basicauth` middleware — written **in clear text** for audit-log use cases. Avoid this tag if your usernames are PII. | | `${api-key}` | `keyauth` middleware, redacted to a 4-byte prefix | | `${csrf-token}` | `csrf` middleware, redacted to a 4-byte prefix | | `${session-id}` | `session` middleware, redacted to a 4-byte prefix | | `${value:key}` | Any bound value with `Value(key)` or `UserValue(key)` lookup methods | :::caution `${value:KEY}` looks up arbitrary context values. CR, LF, NUL, and other ASCII control bytes (except tab) are replaced with spaces before they reach the log line, so attacker-controlled values cannot forge log lines via header smuggling. The lookup still writes the value verbatim apart from that scrub — strip or hash sensitive fields before storing them on the context if you do not want them in operator logs. ::: ### Custom Context Tags Register custom tags with `log.RegisterContextTag`, then reference them from a format passed to `SetContextTemplate`. The built-in `${value:key}` tag is reserved for context-value lookups and cannot be overridden. ```go type tenantContextKey struct{} var tenantKey tenantContextKey log.MustRegisterContextTag("tenant", func(output log.Buffer, ctx any, _ *log.ContextData, _ string) (int, error) { tenant, _ := fiber.ValueFromContext[string](ctx, tenantKey) return output.WriteString(tenant) }) log.MustSetContextTemplate(log.ContextConfig{Format: "[${tenant}] "}) app.Use(func(c fiber.Ctx) error { fiber.StoreInContext(c, tenantKey, "acme") return c.Next() }) ``` :::note Register tags **before** referencing them in `SetContextTemplate`. Compiling a format that references an unregistered name returns `*logtemplate.UnknownTagError` (or panics from `MustSetContextTemplate`). ::: ## Logger Use `Logger` to access the underlying logger and call its native methods: ```go logger := fiberlog.DefaultLogger[*log.Logger]() // Get the default logger instance stdlogger := logger.Logger() // stdlogger is *log.Logger stdlogger.SetFlags(0) // Hide timestamp by setting flags to 0 ``` --- ## 🔄 Redirect Redirect helpers send the client to another URL or route. ## Redirect Methods ### To Redirects to a URL built from the given path. Optionally set an HTTP [status](#status). :::info If unspecified, status defaults to **303 See Other**. ::: ```go title="Signature" func (r *Redirect) To(location string) error ``` ```go title="Example" app.Get("/coffee", func(c fiber.Ctx) error { // => HTTP - GET 301 /teapot return c.Redirect().Status(fiber.StatusMovedPermanently).To("/teapot") }) app.Get("/teapot", func(c fiber.Ctx) error { return c.Status(fiber.StatusTeapot).Send("🍵 short and stout 🍵") }) ``` ```go title="More examples" app.Get("/", func(c fiber.Ctx) error { // => HTTP - GET 303 /foo/bar return c.Redirect().To("/foo/bar") // => HTTP - GET 303 ../login return c.Redirect().To("../login") // => HTTP - GET 303 http://example.com return c.Redirect().To("http://example.com") // => HTTP - GET 301 https://example.com return c.Redirect().Status(301).To("http://example.com") }) ``` ### Route Redirects to a named route with parameters and queries. :::info To send params and queries to a route, use the [`RedirectConfig`](#redirectconfig) struct. ::: ```go title="Signature" func (r *Redirect) Route(name string, config ...RedirectConfig) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { // /user/fiber return c.Redirect().Route("user", fiber.RedirectConfig{ Params: fiber.Map{ "name": "fiber", }, }) }) app.Get("/with-queries", func(c fiber.Ctx) error { // /user/fiber?data[0][name]=john&data[0][age]=10&test=doe return c.Redirect().Route("user", fiber.RedirectConfig{ Params: fiber.Map{ "name": "fiber", }, Queries: map[string]string{ "data[0][name]": "john", "data[0][age]": "10", "test": "doe", }, }) }) app.Get("/user/:name", func(c fiber.Ctx) error { return c.SendString(c.Params("name")) }).Name("user") ``` ### Back Redirects to the referer. If it's missing, fall back to the provided URL. You can also set the status code. :::info If unspecified, status defaults to **303 See Other**. ::: ```go title="Signature" func (r *Redirect) Back(fallback string) error ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Home page") }) app.Get("/test", func(c fiber.Ctx) error { c.Set("Content-Type", "text/html") return c.SendString(`Back`) }) app.Get("/back", func(c fiber.Ctx) error { return c.Redirect().Back("/") }) ``` ## Controls :::info Methods are **chainable**. ::: ### Status Sets the HTTP status code for the redirect. :::info It is used in conjunction with [**To**](#to), [**Route**](#route), and [**Back**](#back) methods. ::: ```go title="Signature" func (r *Redirect) Status(status int) *Redirect ``` ```go title="Example" app.Get("/coffee", func(c fiber.Ctx) error { // => HTTP - GET 301 /teapot return c.Redirect().Status(fiber.StatusMovedPermanently).To("/teapot") }) ``` ### RedirectConfig Sets the configuration for the redirect. :::info It is used in conjunction with the [**Route**](#route) method. ::: ```go title="Definition" // RedirectConfig is a config to use with Redirect().Route() type RedirectConfig struct { Params fiber.Map // Route parameters Queries map[string]string // Query map } ``` ### Flash Message Similar to [Laravel](https://laravel.com/docs/11.x/redirects#redirecting-with-flashed-session-data), we can flash a message and retrieve it in the next request. #### Messages Retrieve all flash messages. See [With](#with) for details. ```go title="Signature" func (r *Redirect) Messages() []FlashMessage ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { messages := c.Redirect().Messages() return c.JSON(messages) }) ``` #### Message Get a flash message by key; see [With](#with). ```go title="Signature" func (r *Redirect) Message(key string) FlashMessage ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { message := c.Redirect().Message("status") return c.SendString(message.Value) }) ``` #### OldInputs Retrieve stored input data. See [WithInput](#withinput). ```go title="Signature" func (r *Redirect) OldInputs() []OldInputData ``` ```go title="Example" app.Get("/", func(c fiber.Ctx) error { oldInputs := c.Redirect().OldInputs() return c.JSON(oldInputs) }) ``` #### OldInput Get stored input data by key; see [WithInput](#withinput). ```go title="Signature" func (r *Redirect) OldInput(key string) OldInputData ``` ```go title="Example" app.Get("/name", func(c fiber.Ctx) error { oldInput := c.Redirect().OldInput("name") return c.SendString(oldInput.Value) }) ``` #### With Send flash messages with `With`. ```go title="Signature" func (r *Redirect) With(key, value string) *Redirect ``` ```go title="Example" app.Get("/login", func(c fiber.Ctx) error { return c.Redirect().With("status", "Logged in successfully").To("/") }) app.Get("/", func(c fiber.Ctx) error { // => Logged in successfully return c.SendString(c.Redirect().Message("status")) }) ``` #### WithInput Send input data with `WithInput`, which stores them in a cookie. It captures form, multipart, or query data depending on the request content type. ```go title="Signature" func (r *Redirect) WithInput() *Redirect ``` ```go title="Example" // curl -X POST http://localhost:3000/login -d "name=John" app.Post("/login", func(c fiber.Ctx) error { return c.Redirect().WithInput().Route("name") }) app.Get("/name", func(c fiber.Ctx) error { // => John return c.SendString(c.Redirect().OldInput("name")) }).Name("name") ``` --- ## 🧩 Services Services wrap external dependencies. Register them in the application's state, and Fiber starts and stops them automatically—useful during development and testing. After adding a service to the app configuration, Fiber starts it on launch and stops it during shutdown. Retrieve a service from state with `GetService` or `MustGetService` (see [State Management](./state)). ## Service Interface The `Service` interface defines methods a service must implement. ### Definition ```go type Service interface { // Start starts the service, returning an error if it fails. Start(ctx context.Context) error // String returns a string representation of the service. // It is used to print a human-readable name of the service in the startup message. String() string // State returns the current state of the service. State(ctx context.Context) (string, error) // Terminate terminates the service, returning an error if it fails. Terminate(ctx context.Context) error } ``` ## Service Methods ### Start Starts the service. Fiber calls this when the application starts. ```go func (s *SomeService) Start(ctx context.Context) error ``` ### String Returns a string representation of the service, used to print the service in the startup message. ```go func (s *SomeService) String() string ``` ### State Reports the current state of the service for the startup message. ```go func (s *SomeService) State(ctx context.Context) (string, error) ``` ### Terminate Stops the service after the application shuts down using a post-shutdown hook. ```go func (s *SomeService) Terminate(ctx context.Context) error ``` ## Comprehensive Examples ### Example: Adding a Service This example demonstrates how to add a Redis store as a service to the application, backed by the Testcontainers Redis Go module. ```go package main import ( "context" "fmt" "log" "time" "github.com/gofiber/fiber/v3" "github.com/redis/go-redis/v9" tcredis "github.com/testcontainers/testcontainers-go/modules/redis" ) const redisServiceName = "redis-store" type redisService struct { ctr *tcredis.RedisContainer } // Start initializes and starts the service. It implements the [fiber.Service] interface. func (s *redisService) Start(ctx context.Context) error { // start the service c, err := tcredis.Run(ctx, "redis:latest") if err != nil { return err } s.ctr = c return nil } // String returns a string representation of the service. // It is used to print a human-readable name of the service in the startup message. // It implements the [fiber.Service] interface. func (s *redisService) String() string { return redisServiceName } // State returns the current state of the service. // It implements the [fiber.Service] interface. func (s *redisService) State(ctx context.Context) (string, error) { state, err := s.ctr.State(ctx) if err != nil { return "", fmt.Errorf("container state: %w", err) } return state.Status, nil } // Terminate stops and removes the service. It implements the [fiber.Service] interface. func (s *redisService) Terminate(ctx context.Context) error { // stop the service return s.ctr.Terminate(ctx) } func main() { cfg := &fiber.Config{} // Initialize service. cfg.Services = append(cfg.Services, &redisService{}) // Define a context provider for the services startup. // This is useful to cancel the startup of the services if the context is canceled. // Default is context.Background(). startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cfg.ServicesStartupContextProvider = func() context.Context { return startupCtx } // Define a context provider for the services shutdown. // This is useful to cancel the shutdown of the services if the context is canceled. // Default is context.Background(). shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cfg.ServicesShutdownContextProvider = func() context.Context { return shutdownCtx } app := fiber.New(*cfg) ctx := context.Background() // Obtain the Redis service from the application's State. redisSrv, ok := fiber.GetService[*redisService](app.State(), redisServiceName) if !ok || redisSrv == nil { log.Printf("Redis service not found") return } // Obtain the connection string from the service. connString, err := redisSrv.ctr.ConnectionString(ctx) if err != nil { log.Printf("Could not get connection string: %v", err) return } // Parse the connection string to create a Redis client. options, err := redis.ParseURL(connString) if err != nil { log.Printf("failed to parse connection string: %s", err) return } // Initialize the Redis client. rdb := redis.NewClient(options) // Check the Redis connection. if err := rdb.Ping(ctx).Err(); err != nil { log.Fatalf("Could not connect to Redis: %v", err) } app.Listen(":3000") } ``` ### Example: Add a service with the Store middleware This example shows how to use services with the Store middleware for dependency injection. It uses a Redis store backed by the Testcontainers Redis module. ```go package main import ( "context" "encoding/json" "fmt" "log" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" redisStore "github.com/gofiber/storage/redis/v3" "github.com/redis/go-redis/v9" tcredis "github.com/testcontainers/testcontainers-go/modules/redis" ) const ( redisServiceName = "redis-store" ) type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` } type redisService struct { ctr *tcredis.RedisContainer } // Start initializes and starts the service. It implements the [fiber.Service] interface. func (s *redisService) Start(ctx context.Context) error { // start the service c, err := tcredis.Run(ctx, "redis:latest") if err != nil { return err } s.ctr = c return nil } // String returns a string representation of the service. // It is used to print a human-readable name of the service in the startup message. // It implements the [fiber.Service] interface. func (s *redisService) String() string { return redisServiceName } // State returns the current state of the service. // It implements the [fiber.Service] interface. func (s *redisService) State(ctx context.Context) (string, error) { state, err := s.ctr.State(ctx) if err != nil { return "", fmt.Errorf("container state: %w", err) } return state.Status, nil } // Terminate stops and removes the service. It implements the [fiber.Service] interface. func (s *redisService) Terminate(ctx context.Context) error { // stop the service return s.ctr.Terminate(ctx) } func main() { cfg := &fiber.Config{} // Initialize service. cfg.Services = append(cfg.Services, &redisService{}) // Define a context provider for the services startup. // This is useful to cancel the startup of the services if the context is canceled. // Default is context.Background(). startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cfg.ServicesStartupContextProvider = func() context.Context { return startupCtx } // Define a context provider for the services shutdown. // This is useful to cancel the shutdown of the services if the context is canceled. // Default is context.Background(). shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cfg.ServicesShutdownContextProvider = func() context.Context { return shutdownCtx } app := fiber.New(*cfg) // Initialize default config app.Use(logger.New()) ctx := context.Background() // Obtain the Redis service from the application's State. redisSrv, ok := fiber.GetService[*redisService](app.State(), redisServiceName) if !ok || redisSrv == nil { log.Printf("Redis service not found") return } // Obtain the connection string from the service. connString, err := redisSrv.ctr.ConnectionString(ctx) if err != nil { log.Printf("Could not get connection string: %v", err) return } // define a GoFiber session store, backed by the Redis service store := redisStore.New(redisStore.Config{ URL: connString, }) app.Post("/user/create", func(c fiber.Ctx) error { var user User if err := c.Bind().JSON(&user); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } json, err := json.Marshal(user) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } // Save the user to the database. err = store.Set(user.Email, json, time.Hour*24) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return c.JSON(user) }) app.Get("/user/:id", func(c fiber.Ctx) error { id := c.Params("id") user, err := store.Get(id) if err == redis.Nil { return c.Status(fiber.StatusNotFound).SendString("User not found") } else if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return c.JSON(string(user)) }) app.Listen(":3000") } ``` --- ## 🗂️ State Management State management provides a global key–value store for application dependencies and runtime data. The store is shared across the entire application and persists between requests. It's commonly used to store [Services](../api/services), which you can retrieve with the `GetService` or `MustGetService` functions. :::warning When prefork is enabled, each worker process has an independent state store, meaning state is not shared between them. ::: Use the index to jump straight to any state method; filter by name or by category: ## SharedState (Prefork-safe) For data that must be shared across prefork workers or multiple app processes, use `app.SharedState()` backed by `fiber.Storage`. Configure storage in `fiber.Config`: ```go app := fiber.New(fiber.Config{ AppName: "billing-api", SharedStorage: redisStorage, // any implementation of fiber.Storage SharedStatePrefix: "billing-shared-", // optional }) ``` If `SharedStatePrefix` is empty, Fiber derives a default namespace and includes `AppName` (when set) to reduce collisions between apps/services. MsgPack and CBOR helpers require the corresponding `Config` encoders/decoders to be configured. If they are unavailable, the helper methods return an error instead of panicking. :::warning Memory storage caveat `SharedState` is only cross-worker / cross-process when the configured `SharedStorage` backend is shared. If you use an in-memory backend (for example memory storage), data remains process-local. In prefork mode, each worker process has its own independent in-memory store. ::: ### SharedState Methods ```go title="Signature" func (app *App) SharedState() *SharedState func (s *SharedState) Set(key string, val []byte, ttl time.Duration) error func (s *SharedState) SetWithContext(ctx context.Context, key string, val []byte, ttl time.Duration) error func (s *SharedState) Get(key string) (val []byte, found bool, err error) func (s *SharedState) GetWithContext(ctx context.Context, key string) (val []byte, found bool, err error) func (s *SharedState) SetJSON(key string, v any, ttl time.Duration) error func (s *SharedState) SetJSONWithContext(ctx context.Context, key string, v any, ttl time.Duration) error func (s *SharedState) GetJSON(key string, out any) (raw []byte, found bool, err error) func (s *SharedState) GetJSONWithContext(ctx context.Context, key string, out any) (raw []byte, found bool, err error) func (s *SharedState) SetMsgPack(key string, v any, ttl time.Duration) error func (s *SharedState) SetMsgPackWithContext(ctx context.Context, key string, v any, ttl time.Duration) error func (s *SharedState) GetMsgPack(key string, out any) (raw []byte, found bool, err error) func (s *SharedState) GetMsgPackWithContext(ctx context.Context, key string, out any) (raw []byte, found bool, err error) func (s *SharedState) SetCBOR(key string, v any, ttl time.Duration) error func (s *SharedState) SetCBORWithContext(ctx context.Context, key string, v any, ttl time.Duration) error func (s *SharedState) GetCBOR(key string, out any) (raw []byte, found bool, err error) func (s *SharedState) GetCBORWithContext(ctx context.Context, key string, out any) (raw []byte, found bool, err error) func (s *SharedState) SetXML(key string, v any, ttl time.Duration) error func (s *SharedState) SetXMLWithContext(ctx context.Context, key string, v any, ttl time.Duration) error func (s *SharedState) GetXML(key string, out any) (raw []byte, found bool, err error) func (s *SharedState) GetXMLWithContext(ctx context.Context, key string, out any) (raw []byte, found bool, err error) func (s *SharedState) Delete(key string) error func (s *SharedState) DeleteWithContext(ctx context.Context, key string) error func (s *SharedState) Has(key string) (bool, error) func (s *SharedState) HasWithContext(ctx context.Context, key string) (bool, error) func (s *SharedState) Reset() error func (s *SharedState) ResetWithContext(ctx context.Context) error func (s *SharedState) Close() error ``` ### SharedState Example ```go type SessionSnapshot struct { UserID string `json:"user_id"` UpdatedAt time.Time `json:"updated_at"` } app.Post("/sessions/:id", func(c fiber.Ctx) error { key := "session:" + c.Params("id") value := SessionSnapshot{ UserID: c.Params("id"), UpdatedAt: time.Now().UTC(), } if err := app.SharedState().SetJSON(key, value, 30*time.Minute); err != nil { return err } return c.SendStatus(fiber.StatusAccepted) }) app.Get("/sessions/:id", func(c fiber.Ctx) error { key := "session:" + c.Params("id") var snapshot SessionSnapshot _, found, err := app.SharedState().GetJSON(key, &snapshot) if err != nil { return err } if !found { return c.SendStatus(fiber.StatusNotFound) } return c.JSON(snapshot) }) ``` ### SharedState with Context (timeouts/cancellation) ```go ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() err := app.SharedState().SetJSONWithContext(ctx, "job:42", fiber.Map{ "status": "queued", }, 2*time.Minute) if err != nil { // timeout, cancellation, storage error, or JSON serialization error } ``` ## State Type `State` is a key–value store built on top of `sync.Map` to ensure safe concurrent access. It allows storage and retrieval of dependencies and configurations in a Fiber application as well as thread–safe access to runtime data. ### Definition ```go // State is a key–value store for Fiber's app, used as a global storage for the app's dependencies. // It is a thread–safe implementation of a map[string]any, using sync.Map. type State struct { dependencies sync.Map } ``` ## Methods on State ### Set Set adds or updates a key–value pair in the State. ```go // Set adds or updates a key–value pair in the State. func (s *State) Set(key string, value any) ``` **Usage Example:** ```go app.State().Set("appName", "My Fiber App") ``` ### Get Get retrieves a value from the State. ```go title="Signature" func (s *State) Get(key string) (any, bool) ``` **Usage Example:** ```go value, ok := app.State().Get("appName") if ok { fmt.Println("App Name:", value) } ``` ### MustGet MustGet retrieves a value from the State and panics if the key is not found. ```go title="Signature" func (s *State) MustGet(key string) any ``` **Usage Example:** ```go appName := app.State().MustGet("appName") fmt.Println("App Name:", appName) ``` ### Has Has checks if a key exists in the State. ```go title="Signature" func (s *State) Has(key string) bool ``` **Usage Example:** ```go if app.State().Has("appName") { fmt.Println("App Name is set.") } ``` ### Delete Delete removes a key–value pair from the State. ```go title="Signature" func (s *State) Delete(key string) ``` **Usage Example:** ```go app.State().Delete("obsoleteKey") ``` ### Reset Reset removes all keys from the State, including those related to Services. ```go title="Signature" func (s *State) Reset() ``` **Usage Example:** ```go app.State().Reset() ``` ### Keys Keys returns a slice containing all keys present in the State. ```go title="Signature" func (s *State) Keys() []string ``` **Usage Example:** ```go keys := app.State().Keys() fmt.Println("State Keys:", keys) ``` ### Len Len returns the number of keys in the State. ```go // Len returns the number of keys in the State. func (s *State) Len() int ``` **Usage Example:** ```go fmt.Printf("Total State Entries: %d\n", app.State().Len()) ``` ### GetString GetString retrieves a string value from the State. It returns the string and a boolean indicating a successful type assertion. ```go title="Signature" func (s *State) GetString(key string) (string, bool) ``` **Usage Example:** ```go if appName, ok := app.State().GetString("appName"); ok { fmt.Println("App Name:", appName) } ``` ### GetInt GetInt retrieves an integer value from the State. It returns the int and a boolean indicating a successful type assertion. ```go title="Signature" func (s *State) GetInt(key string) (int, bool) ``` **Usage Example:** ```go if count, ok := app.State().GetInt("userCount"); ok { fmt.Printf("User Count: %d\n", count) } ``` ### GetBool GetBool retrieves a boolean value from the State. It returns the bool and a boolean indicating a successful type assertion. ```go title="Signature" func (s *State) GetBool(key string) (value, bool) ``` **Usage Example:** ```go if debug, ok := app.State().GetBool("debugMode"); ok { fmt.Printf("Debug Mode: %v\n", debug) } ``` ### GetFloat64 GetFloat64 retrieves a float64 value from the State. It returns the float64 and a boolean indicating a successful type assertion. ```go title="Signature" func (s *State) GetFloat64(key string) (float64, bool) ``` **Usage Example:** ```go title="Signature" if ratio, ok := app.State().GetFloat64("scalingFactor"); ok { fmt.Printf("Scaling Factor: %f\n", ratio) } ``` ### GetUint GetUint retrieves a `uint` value from the State. ```go title="Signature" func (s *State) GetUint(key string) (uint, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUint("maxConnections"); ok { fmt.Printf("Max Connections: %d\n", val) } ``` ### GetInt8 GetInt8 retrieves an `int8` value from the State. ```go title="Signature" func (s *State) GetInt8(key string) (int8, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetInt8("threshold"); ok { fmt.Printf("Threshold: %d\n", val) } ``` ### GetInt16 GetInt16 retrieves an `int16` value from the State. ```go title="Signature" func (s *State) GetInt16(key string) (int16, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetInt16("minValue"); ok { fmt.Printf("Minimum Value: %d\n", val) } ``` ### GetInt32 GetInt32 retrieves an `int32` value from the State. ```go title="Signature" func (s *State) GetInt32(key string) (int32, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetInt32("portNumber"); ok { fmt.Printf("Port Number: %d\n", val) } ``` ### GetInt64 GetInt64 retrieves an `int64` value from the State. ```go title="Signature" func (s *State) GetInt64(key string) (int64, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetInt64("fileSize"); ok { fmt.Printf("File Size: %d\n", val) } ``` ### GetUint8 GetUint8 retrieves a `uint8` value from the State. ```go title="Signature" func (s *State) GetUint8(key string) (uint8, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUint8("byteValue"); ok { fmt.Printf("Byte Value: %d\n", val) } ``` ### GetUint16 GetUint16 retrieves a `uint16` value from the State. ```go title="Signature" func (s *State) GetUint16(key string) (uint16, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUint16("limit"); ok { fmt.Printf("Limit: %d\n", val) } ``` ### GetUint32 GetUint32 retrieves a `uint32` value from the State. ```go title="Signature" func (s *State) GetUint32(key string) (uint32, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUint32("timeout"); ok { fmt.Printf("Timeout: %d\n", val) } ``` ### GetUint64 GetUint64 retrieves a `uint64` value from the State. ```go title="Signature" func (s *State) GetUint64(key string) (uint64, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUint64("maxSize"); ok { fmt.Printf("Max Size: %d\n", val) } ``` ### GetUintptr GetUintptr retrieves a `uintptr` value from the State. ```go title="Signature" func (s *State) GetUintptr(key string) (uintptr, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetUintptr("pointerValue"); ok { fmt.Printf("Pointer Value: %d\n", val) } ``` ### GetFloat32 GetFloat32 retrieves a `float32` value from the State. ```go title="Signature" func (s *State) GetFloat32(key string) (float32, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetFloat32("scalingFactor32"); ok { fmt.Printf("Scaling Factor (float32): %f\n", val) } ``` ### GetComplex64 GetComplex64 retrieves a `complex64` value from the State. ```go title="Signature" func (s *State) GetComplex64(key string) (complex64, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetComplex64("complexVal"); ok { fmt.Printf("Complex Value (complex64): %v\n", val) } ``` ### GetComplex128 GetComplex128 retrieves a `complex128` value from the State. ```go title="Signature" func (s *State) GetComplex128(key string) (complex128, bool) ``` **Usage Example:** ```go if val, ok := app.State().GetComplex128("complexVal128"); ok { fmt.Printf("Complex Value (complex128): %v\n", val) } ``` ## Generic Functions Fiber provides generic functions to retrieve state values with type safety and fallback options. ### GetState GetState retrieves a value from the State and casts it to the desired type. It returns the cast value and a boolean indicating if the cast was successful. ```go title="Signature" func GetState[T any](s *State, key string) (T, bool) ``` **Usage Example:** ```go // Retrieve an integer value safely. userCount, ok := GetState[int](app.State(), "userCount") if ok { fmt.Printf("User Count: %d\n", userCount) } ``` ### MustGetState MustGetState retrieves a value from the State and casts it to the desired type. It panics if the key is not found or if the type assertion fails. ```go title="Signature" func MustGetState[T any](s *State, key string) T ``` **Usage Example:** ```go // Retrieve the value or panic if it is not present. config := MustGetState[string](app.State(), "configFile") fmt.Println("Config File:", config) ``` ### GetStateWithDefault GetStateWithDefault retrieves a value from the State, casting it to the desired type. If the key is not present, it returns the provided default value. ```go title="Signature" func GetStateWithDefault[T any](s *State, key string, defaultVal T) T ``` **Usage Example:** ```go // Retrieve a value with a fallback. requestCount := GetStateWithDefault[int](app.State(), "requestCount", 0) fmt.Printf("Request Count: %d\n", requestCount) ``` ### GetService GetService retrieves a Service from the State and casts it to the desired type. It returns the cast value and a boolean indicating if the cast was successful. ```go title="Signature" func GetService[T Service](s *State, key string) (T, bool) { ``` **Usage Example:** ```go if srv, ok := fiber.GetService[*redisService](app.State(), "someService") fmt.Printf("Some Service: %s\n", srv.String()) } ``` ### MustGetService MustGetService retrieves a Service from the State and casts it to the desired type. It panics if the key is not found or if the type assertion fails. ```go title="Signature" func MustGetService[T Service](s *State, key string) T ``` **Usage Example:** ```go srv := fiber.MustGetService[*SomeService](app.State(), "someService") ``` ## Comprehensive Examples ### Example: Request Counter This example demonstrates how to track the number of requests using the State. ```go package main import ( "fmt" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Initialize state with a counter. app.State().Set("requestCount", 0) // Middleware: Increase counter for every request. app.Use(func(c fiber.Ctx) error { count, _ := c.App().State().GetInt("requestCount") app.State().Set("requestCount", count+1) return c.Next() }) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello World!") }) app.Get("/stats", func(c fiber.Ctx) error { count, _ := c.App().State().Get("requestCount") return c.SendString(fmt.Sprintf("Total requests: %d", count)) }) app.Listen(":3000") } ``` ### Example: Environment–Specific Configuration This example shows how to configure different settings based on the environment. ```go package main import ( "os" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Determine environment. environment := os.Getenv("ENV") if environment == "" { environment = "development" } app.State().Set("environment", environment) // Set environment-specific configurations. if environment == "development" { app.State().Set("apiUrl", "http://localhost:8080/api") app.State().Set("debug", true) } else { app.State().Set("apiUrl", "https://api.production.com") app.State().Set("debug", false) } app.Get("/config", func(c fiber.Ctx) error { config := map[string]any{ "environment": environment, "apiUrl": fiber.GetStateWithDefault(c.App().State(), "apiUrl", ""), "debug": fiber.GetStateWithDefault(c.App().State(), "debug", false), } return c.JSON(config) }) app.Listen(":3000") } ``` ### Example: Dependency Injection with State Management This example demonstrates how to use the State for dependency injection in a Fiber application. ```go package main import ( "context" "fmt" "log" "github.com/gofiber/fiber/v3" "github.com/redis/go-redis/v9" ) type User struct { ID int `query:"id"` Name string `query:"name"` Email string `query:"email"` } func main() { app := fiber.New() ctx := context.Background() // Initialize Redis client. rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) // Check the Redis connection. if err := rdb.Ping(ctx).Err(); err != nil { log.Fatalf("Could not connect to Redis: %v", err) } // Inject the Redis client into Fiber's State for dependency injection. app.State().Set("redis", rdb) app.Get("/user/create", func(c fiber.Ctx) error { var user User if err := c.Bind().Query(&user); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } // Retrieve the Redis client from the global state. rdb, ok := fiber.GetState[*redis.Client](c.App().State(), "redis") if !ok { return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found") } // Save the user to the database. key := fmt.Sprintf("user:%d", user.ID) err := rdb.HSet(ctx, key, "name", user.Name, "email", user.Email).Err() if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return c.JSON(user) }) app.Get("/user/:id", func(c fiber.Ctx) error { id := c.Params("id") rdb, ok := fiber.GetState[*redis.Client](c.App().State(), "redis") if !ok { return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found") } key := fmt.Sprintf("user:%s", id) user, err := rdb.HGetAll(ctx, key).Result() if err == redis.Nil { return c.Status(fiber.StatusNotFound).SendString("User not found") } else if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return c.JSON(user) }) app.Listen(":3000") } ``` --- ## 🍳 Examples ## Basic Auth Clients send credentials via the `Authorization` header, while the server stores hashed passwords as shown in the middleware example. ```go package main import ( "encoding/base64" "fmt" "github.com/gofiber/fiber/v3/client" ) func main() { cc := client.New() out := base64.StdEncoding.EncodeToString([]byte("john:doe")) resp, err := cc.Get("http://localhost:3000", client.Config{ Header: map[string]string{ "Authorization": "Basic " + out, }, }) if err != nil { panic(err) } fmt.Print(string(resp.Body())) } ``` ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/basicauth" ) func main() { app := fiber.New() app.Use( basicauth.New(basicauth.Config{ Users: map[string]string{ // "doe" hashed using SHA-256 "john": "{SHA256}eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=", }, }), ) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Listen(":3000") } ``` ## TLS ```go package main import ( "crypto/tls" "crypto/x509" "fmt" "os" "github.com/gofiber/fiber/v3/client" ) func main() { cc := client.New() certPool, err := x509.SystemCertPool() if err != nil { panic(err) } cert, err := os.ReadFile("ssl.cert") if err != nil { panic(err) } certPool.AppendCertsFromPEM(cert) cc.SetTLSConfig(&tls.Config{ RootCAs: certPool, }) resp, err := cc.Get("https://localhost:3000") if err != nil { panic(err) } fmt.Print(string(resp.Body())) } ``` ```go 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!") }) err := app.Listen(":3000", fiber.ListenConfig{ CertFile: "ssl.cert", CertKeyFile: "ssl.key", }) if err != nil { panic(err) } } ``` ## Reusing fasthttp transports The Fiber client can wrap existing `fasthttp` clients so that you can reuse connection pools, custom dialers, or load-balancing logic that is already tuned for your infrastructure. ### HostClient ```go package main import ( "log" "time" "github.com/gofiber/fiber/v3/client" "github.com/valyala/fasthttp" ) func main() { hc := &fasthttp.HostClient{ Addr: "api.internal:443", IsTLS: true, MaxConnDuration: 30 * time.Second, MaxIdleConnDuration: 10 * time.Second, } cc := client.NewWithHostClient(hc) resp, err := cc.Get("https://api.internal:443/status") if err != nil { log.Fatal(err) } log.Printf("status=%d body=%s", resp.StatusCode(), resp.Body()) } ``` ### LBClient ```go package main import ( "log" "time" "github.com/gofiber/fiber/v3/client" "github.com/valyala/fasthttp" ) func main() { lb := &fasthttp.LBClient{ Timeout: 2 * time.Second, Clients: []fasthttp.BalancingClient{ &fasthttp.HostClient{Addr: "edge-1.internal:8080"}, &fasthttp.HostClient{Addr: "edge-2.internal:8080"}, }, } cc := client.NewWithLBClient(lb) // Per-request overrides such as redirects, retries, TLS, and proxy dialers // are shared across every host client managed by the load balancer. resp, err := cc.Get("http://service.internal/api") if err != nil { log.Fatal(err) } log.Printf("status=%d body=%s", resp.StatusCode(), resp.Body()) } ``` ## Cookie jar The client can store and reuse cookies between requests by attaching a cookie jar. The jar follows RFC 6265 for storage and retrieval: - A `Set-Cookie` without a `Path` attribute is scoped to the **directory** of the request that set it, not to the whole host. A cookie set by a response to `/api/login` defaults to `Path=/api` and is not sent to `/`. Send an explicit `Path=/` to scope it host-wide. - Cookies are identified by the triple (name, domain, path), so the same name can be stored at several paths at once. When more than one applies to a request, the one with the longest path wins. - Storage is bounded: at most 1024 storage keys, and at most 64 cookies per key. A host-only cookie and a `Domain=` cookie are stored under different keys, so a single host can occupy more than one. When a key is full the jar drops expired entries first, then the least recently written — a session cookie the server re-sends on each response is not evicted by a flood of one-off cookies. A single request carries at most 64 cookies, the most specific first, so a host cannot inflate the `Cookie` header by spreading cookies across the `Domain=` keys of its parent labels. - Cookies are attached once per request, before any redirect is followed, so a redirect chain carries the cookies selected for the original URL. ### Request ```go func main() { jar := client.AcquireCookieJar() defer client.ReleaseCookieJar(jar) cc := client.New() cc.SetCookieJar(jar) jar.SetKeyValueBytes("httpbin.org", []byte("john"), []byte("doe")) resp, err := cc.Get("https://httpbin.org/cookies") if err != nil { panic(err) } fmt.Println(string(resp.Body())) } ```
Click here to see the result ```json { "cookies": { "john": "doe" } } ```
### Response Read cookies set by the server directly from the jar. ```go func main() { jar := client.AcquireCookieJar() defer client.ReleaseCookieJar(jar) cc := client.New() cc.SetCookieJar(jar) _, err := cc.Get("https://httpbin.org/cookies/set/john/doe") if err != nil { panic(err) } uri := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(uri) uri.SetHost("httpbin.org") uri.SetPath("/cookies") fmt.Println(jar.Get(uri)) } ```
Click here to see the result ```plaintext [john=doe; path=/] ```
### Response (follow-up request) ```go func main() { jar := client.AcquireCookieJar() defer client.ReleaseCookieJar(jar) cc := client.New() cc.SetCookieJar(jar) _, err := cc.Get("https://httpbin.org/cookies/set/john/doe") if err != nil { panic(err) } resp, err := cc.Get("https://httpbin.org/cookies") if err != nil { panic(err) } fmt.Println(resp.String()) } ```
Click here to see the result ```json { "cookies": { "john": "doe" } } ```
--- ## 🎣 Hooks(Client) Hooks let you intercept and modify the request or response flow of the Fiber client. They are useful for: - Changing request parameters (e.g., URL, headers) before sending the request. - Logging request and response details. - Integrating complex tracing or monitoring tools. - Handling authentication, retries, or other custom logic. There are two kinds of hooks: ## Request Hooks **Request hooks** are functions executed before the HTTP request is sent. They follow the signature: ```go type RequestHook func(*Client, *Request) error ``` A request hook receives both the `Client` and the `Request` objects, allowing you to modify the request before it leaves your application. For example, you could: - Change the host URL. - Log request details (method, URL, headers). - Add or modify headers or query parameters. - Intercept and apply custom authentication logic. **Example:** ```go type Repository struct { Name string `json:"name"` FullName string `json:"full_name"` Description string `json:"description"` Homepage string `json:"homepage"` Owner struct { Login string `json:"login"` } `json:"owner"` } func main() { cc := client.New() // Add a request hook that modifies the request URL before sending. cc.AddRequestHook(func(c *client.Client, r *client.Request) error { r.SetURL("https://api.github.com/" + r.URL()) return nil }) resp, err := cc.Get("repos/gofiber/fiber") if err != nil { panic(err) } var repo Repository if err := resp.JSON(&repo); err != nil { panic(err) } fmt.Printf("Status code: %d\n", resp.StatusCode()) fmt.Printf("Repository: %s\n", repo.FullName) fmt.Printf("Description: %s\n", repo.Description) fmt.Printf("Homepage: %s\n", repo.Homepage) fmt.Printf("Owner: %s\n", repo.Owner.Login) fmt.Printf("Name: %s\n", repo.Name) fmt.Printf("Full Name: %s\n", repo.FullName) } ```
Click here to see the result ```plaintext Status code: 200 Repository: gofiber/fiber Description: ⚡️ Express inspired web framework written in Go Homepage: https://gofiber.io Owner: gofiber Name: fiber Full Name: gofiber/fiber ```
### Built-in Request Hooks Fiber includes built-in request hooks: - **parserRequestURL**: Normalizes and customizes the URL based on path and query parameters. Required for `PathParam` and `QueryParam` methods. - **parserRequestHeader**: Sets request headers, cookies, content type, referer, and user agent based on client and request properties. - **parserRequestBody**: Automatically serializes the request body (JSON, XML, form, file uploads, etc.). :::info If a request hook returns an error, Fiber stops the request and returns the error immediately. ::: **Example with Multiple Hooks:** ```go func main() { cc := client.New() cc.AddRequestHook(func(c *client.Client, r *client.Request) error { fmt.Println("Hook 1") return errors.New("error") }) cc.AddRequestHook(func(c *client.Client, r *client.Request) error { fmt.Println("Hook 2") return nil }) _, err := cc.Get("https://example.com/") if err != nil { panic(err) } } ```
Click here to see the result ```shell Hook 1. panic: error goroutine 1 [running]: main.main() main.go:25 +0xaa exit status 2 ```
## Response Hooks **Response hooks** are functions executed after the HTTP response is received. They follow the signature: ```go type ResponseHook func(*Client, *Response, *Request) error ``` A response hook receives the `Client`, `Response`, and `Request` objects, allowing you to inspect and modify the response or perform additional actions such as logging, tracing, or processing response data. **Example:** ```go func main() { cc := client.New() cc.AddResponseHook(func(c *client.Client, resp *client.Response, req *client.Request) error { fmt.Printf("Response Status Code: %d\n", resp.StatusCode()) fmt.Printf("HTTP protocol: %s\n\n", resp.Protocol()) fmt.Println("Response Headers:") for key, value := range resp.RawResponse.Header.All() { fmt.Printf("%s: %s\n", key, value) } return nil }) _, err := cc.Get("https://example.com/") if err != nil { panic(err) } } ```
Click here to see the result ```plaintext Response Status Code: 200 HTTP protocol: HTTP/1.1 Response Headers: Content-Length: 1256 Content-Type: text/html; charset=UTF-8 Server: ECAcc (dcd/7D5A) Age: 216114 Cache-Control: max-age=604800 Date: Fri, 10 May 2024 10:49:10 GMT Etag: "3147526947+gzip+ident" Expires: Fri, 17 May 2024 10:49:10 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Vary: Accept-Encoding X-Cache: HIT ```
### Built-in Response Hooks Fiber includes built-in response hooks: - **parserResponseCookie**: Parses cookies from the response and stores them in the response object and cookie jar if available. - **logger**: Logs information about the raw request and response. It uses the `log.CommonLogger` interface. :::info If a response hook returns an error, Fiber skips the remaining hooks and returns that error. ::: **Example with Multiple Response Hooks:** ```go func main() { cc := client.New() cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error { fmt.Println("Hook 1") return nil }) cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error { fmt.Println("Hook 2") return errors.New("error") }) cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error { fmt.Println("Hook 3") return nil }) _, err := cc.Get("https://example.com/") if err != nil { panic(err) } } ```
Click here to see the result ```shell Hook 1 Hook 2 panic: error goroutine 1 [running]: main.main() main.go:30 +0xd6 exit status 2 ```
## Hook Execution Order Hooks run in FIFO order (first in, first out), so they're executed in the order you add them. Keep this in mind when adding multiple hooks, as the order can affect the outcome. **Example:** ```go func main() { cc := client.New() cc.AddRequestHook(func(c *client.Client, r *client.Request) error { fmt.Println("Hook 1") return nil }) cc.AddRequestHook(func(c *client.Client, r *client.Request) error { fmt.Println("Hook 2") return nil }) _, err := cc.Get("https://example.com/") if err != nil { panic(err) } } ```
Click here to see the result ```plaintext Hook 1 Hook 2 ```
--- ## 📤 Request The `Request` struct in Fiber's HTTP client represents an HTTP request. It encapsulates the data required to send a request, including: - **URL**: The endpoint to which the request is sent. - **Method**: The HTTP method (GET, POST, PUT, DELETE, etc.). - **Headers**: Key-value pairs that provide additional information about the request or guide how the response should be processed. - **Body**: The data sent with the request, commonly used with methods like POST and PUT. - **Query Parameters**: Parameters appended to the URL to pass additional data or modify the request's behavior. This structure is designed to be both flexible and efficient, allowing you to easily build and modify HTTP requests as needed. ```go type Request struct { ctx context.Context body any header Header params QueryParam cookies Cookie path PathParam client *Client formData FormData RawRequest *fasthttp.Request url string method string userAgent string boundary string referer string files []*File timeout time.Duration maxRedirects int bodyType bodyType isPathNormalizingDisabled bool } ``` Use the index to jump straight to any request method; filter by name: ## REST Methods ### Get **Get** sends a GET request to the specified URL. It sets the URL and HTTP method, then dispatches the request to the server. ```go title="Signature" func (r *Request) Get(url string) (*Response, error) ``` ### Post **Post** sends a POST request. It sets the URL and method to POST, then sends the request. ```go title="Signature" func (r *Request) Post(url string) (*Response, error) ``` ### Put **Put** sends a PUT request. It sets the URL and method to PUT, then sends the request. ```go title="Signature" func (r *Request) Put(url string) (*Response, error) ``` ### Patch **Patch** sends a PATCH request. It sets the URL and method to PATCH, then sends the request. ```go title="Signature" func (r *Request) Patch(url string) (*Response, error) ``` ### Delete **Delete** sends a DELETE request. It sets the URL and method to DELETE, then sends the request. ```go title="Signature" func (r *Request) Delete(url string) (*Response, error) ``` ### Head **Head** sends a HEAD request. It sets the URL and method to HEAD, then sends the request. ```go title="Signature" func (r *Request) Head(url string) (*Response, error) ``` ### Options **Options** sends an OPTIONS request. It sets the URL and method to OPTIONS, then sends the request. ```go title="Signature" func (r *Request) Options(url string) (*Response, error) ``` ### Query **Query** sends a QUERY request. It sets the URL and method to QUERY, then sends the request. ```go title="Signature" func (r *Request) Query(url string) (*Response, error) ``` ### Custom **Custom** sends a request using a custom HTTP method. For example, you can use this to send a TRACE or CONNECT request. ```go title="Signature" func (r *Request) Custom(url, method string) (*Response, error) ``` ## AcquireRequest **AcquireRequest** returns a new pooled `Request`. Call `ReleaseRequest` when you're finished to return it to the pool and limit allocations. ```go title="Signature" func AcquireRequest() *Request ``` ## ReleaseRequest **ReleaseRequest** returns the `Request` to the pool. Do not use it after releasing; doing so may cause data races. ```go title="Signature" func ReleaseRequest(req *Request) ``` ## Method **Method** returns the current HTTP method set for the request. ```go title="Signature" func (r *Request) Method() string ``` ## SetMethod **SetMethod** sets the HTTP method for the `Request` object. Typically, you should use the specialized request methods (`Get`, `Post`, etc.) instead of calling `SetMethod` directly. ```go title="Signature" func (r *Request) SetMethod(method string) *Request ``` ## URL **URL** returns the current URL set in the `Request`. ```go title="Signature" func (r *Request) URL() string ``` ## SetURL **SetURL** sets the URL for the `Request` object. ```go title="Signature" func (r *Request) SetURL(url string) *Request ``` ## Client **Client** retrieves the `Client` instance associated with the `Request`. ```go title="Signature" func (r *Request) Client() *Client ``` ## SetClient **SetClient** assigns a `Client` to the `Request`. If the provided client is `nil`, it will panic. ```go title="Signature" func (r *Request) SetClient(c *Client) *Request ``` ## Context **Context** returns the `context.Context` of the request, or `context.Background()` if none is set. ```go title="Signature" func (r *Request) Context() context.Context ``` ## SetContext **SetContext** sets the `context.Context` for the request, allowing you to cancel or time out the request. See the [Go blog](https://blog.golang.org/context) and [context](https://pkg.go.dev/context) docs for more details. ```go title="Signature" func (r *Request) SetContext(ctx context.Context) *Request ``` ## Header **Header** returns all values for the specified header key. It searches all header fields stored in the request. ```go title="Signature" func (r *Request) Header(key string) []string ``` ### Headers **Headers** returns an iterator over all headers in the request. Use `maps.Collect()` to transform them into a map if needed. The returned values are valid only until the request is released. Make copies as required. ```go title="Signature" func (r *Request) Headers() iter.Seq2[string, []string] ```
Example ```go title="Example" req := client.AcquireRequest() req.AddHeader("Golang", "Fiber") req.AddHeader("Test", "123456") req.AddHeader("Test", "654321") for k, v := range req.Headers() { fmt.Printf("Header Key: %s, Header Value: %v\n", k, v) } ``` ```sh Header Key: Golang, Header Value: [Fiber] Header Key: Test, Header Value: [123456 654321] ```
Example with maps.Collect() ```go title="Example with maps.Collect()" req := client.AcquireRequest() req.AddHeader("Golang", "Fiber") req.AddHeader("Test", "123456") req.AddHeader("Test", "654321") headers := maps.Collect(req.Headers()) // Collect all headers into a map for k, v := range headers { fmt.Printf("Header Key: %s, Header Value: %v\n", k, v) } ``` ```sh Header Key: Golang, Header Value: [Fiber] Header Key: Test, Header Value: [123456 654321] ```
### AddHeader **AddHeader** adds a single header field and its value to the request. ```go title="Signature" func (r *Request) AddHeader(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.AddHeader("Golang", "Fiber") req.AddHeader("Test", "123456") req.AddHeader("Test", "654321") resp, err := req.Get("https://httpbin.org/headers") if err != nil { panic(err) } fmt.Println(resp.String()) ``` ```json { "headers": { "Golang": "Fiber", "Host": "httpbin.org", "Referer": "", "Test": "123456,654321", "User-Agent": "fiber", "X-Amzn-Trace-Id": "Root=1-664105d2-033cf7173457adb56d9e7193" } } ```
### SetHeader **SetHeader** sets a single header field and its value, overriding any previously set header with the same key. ```go title="Signature" func (r *Request) SetHeader(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetHeader("Test", "123456") req.SetHeader("Test", "654321") resp, err := req.Get("https://httpbin.org/headers") if err != nil { panic(err) } fmt.Println(resp.String()) ``` ```json { "headers": { "Golang": "Fiber", "Host": "httpbin.org", "Referer": "", "Test": "654321", "User-Agent": "fiber", "X-Amzn-Trace-Id": "Root=1-664105e5-5d676ba348450cdb62847f04" } } ```
### AddHeaders **AddHeaders** adds multiple headers at once from a map of string slices. ```go title="Signature" func (r *Request) AddHeaders(h map[string][]string) *Request ``` ### SetHeaders **SetHeaders** sets multiple headers at once from a map of strings, overriding any previously set headers. ```go title="Signature" func (r *Request) SetHeaders(h map[string]string) *Request ``` ## Param **Param** returns all values associated with a given query parameter key. ```go title="Signature" func (r *Request) Param(key string) []string ``` ### Params **Params** returns an iterator over all query parameters. Use `maps.Collect()` if you need them in a map. The returned values are valid only until the request is released. ```go title="Signature" func (r *Request) Params() iter.Seq2[string, []string] ``` ### AddParam **AddParam** adds a single query parameter key-value pair. ```go title="Signature" func (r *Request) AddParam(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.AddParam("name", "john") req.AddParam("hobbies", "football") req.AddParam("hobbies", "basketball") resp, err := req.Get("https://httpbin.org/response-headers") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "Content-Length": "145", "Content-Type": "application/json", "hobbies": [ "football", "basketball" ], "name": "joe" } ```
### SetParam **SetParam** sets a single query parameter key-value pair, overriding any previously set values for that key. ```go title="Signature" func (r *Request) SetParam(key, val string) *Request ``` ### AddParams **AddParams** adds multiple query parameters from a map of string slices. ```go title="Signature" func (r *Request) AddParams(m map[string][]string) *Request ``` ### SetParams **SetParams** sets multiple query parameters from a map of strings, overriding previously set values. ```go title="Signature" func (r *Request) SetParams(m map[string]string) *Request ``` ### SetParamsWithStruct **SetParamsWithStruct** sets multiple query parameters from a struct. Nested structs are not supported. ```go title="Signature" func (r *Request) SetParamsWithStruct(v any) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetParamsWithStruct(struct { Name string `json:"name"` Hobbies []string `json:"hobbies"` }{ Name: "John Doe", Hobbies: []string{ "Football", "Basketball", }, }) resp, err := req.Get("https://httpbin.org/response-headers") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "Content-Length": "147", "Content-Type": "application/json", "Hobbies": [ "Football", "Basketball" ], "Name": "John Doe" } ```
### DelParams **DelParams** removes one or more query parameters by their keys. ```go title="Signature" func (r *Request) DelParams(key ...string) *Request ``` ## UserAgent **UserAgent** returns the user agent currently set in the request. ```go title="Signature" func (r *Request) UserAgent() string ``` ## SetUserAgent **SetUserAgent** sets the user agent header for the request, overriding the one set at the client level if any. ```go title="Signature" func (r *Request) SetUserAgent(ua string) *Request ``` ## Boundary **Boundary** returns the multipart boundary used by the request. ```go title="Signature" func (r *Request) Boundary() string ``` ## SetBoundary **SetBoundary** sets the multipart boundary for file uploads. ```go title="Signature" func (r *Request) SetBoundary(b string) *Request ``` ## Referer **Referer** returns the Referer header value currently set in the request. ```go title="Signature" func (r *Request) Referer() string ``` ## SetReferer **SetReferer** sets the Referer header for the request, overriding the one set at the client level if any. ```go title="Signature" func (r *Request) SetReferer(referer string) *Request ``` ## Cookie **Cookie** returns the value of the specified cookie. If the cookie does not exist, it returns an empty string. ```go title="Signature" func (r *Request) Cookie(key string) string ``` ### Cookies **Cookies** returns an iterator over all cookies set in the request. Use `maps.Collect()` to gather them into a map. ```go title="Signature" func (r *Request) Cookies() iter.Seq2[string, string] ``` ### SetCookie **SetCookie** sets a single cookie key-value pair, overriding any previously set cookie with the same key. ```go title="Signature" func (r *Request) SetCookie(key, val string) *Request ``` ### SetCookies **SetCookies** sets multiple cookies from a map, overriding previously set values. ```go title="Signature" func (r *Request) SetCookies(m map[string]string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetCookies(map[string]string{ "cookie1": "value1", "cookie2": "value2", }) resp, err := req.Get("https://httpbin.org/cookies") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "cookies": { "test": "123" } } ```
### SetCookiesWithStruct **SetCookiesWithStruct** sets multiple cookies from a struct. ```go title="Signature" func (r *Request) SetCookiesWithStruct(v any) *Request ``` ### DelCookies **DelCookies** removes one or more cookies by their keys. ```go title="Signature" func (r *Request) DelCookies(key ...string) *Request ``` ## PathParam **PathParam** returns the value of a named path parameter. If not found, returns an empty string. ```go title="Signature" func (r *Request) PathParam(key string) string ``` ### PathParams **PathParams** returns an iterator over all path parameters in the request. Use `maps.Collect()` to convert them into a map. ```go title="Signature" func (r *Request) PathParams() iter.Seq2[string, string] ``` ### SetPathParam **SetPathParam** sets a single path parameter key-value pair, overriding previously set values. ```go title="Signature" func (r *Request) SetPathParam(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetPathParam("base64", "R29maWJlcg==") resp, err := req.Get("https://httpbin.org/base64/:base64") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```plaintext Gofiber ```
### SetPathParams **SetPathParams** sets multiple path parameters at once, overriding previously set values. ```go title="Signature" func (r *Request) SetPathParams(m map[string]string) *Request ``` ### SetPathParamsWithStruct **SetPathParamsWithStruct** sets multiple path parameters from a struct. ```go title="Signature" func (r *Request) SetPathParamsWithStruct(v any) *Request ``` ### DelPathParams **DelPathParams** deletes one or more path parameters by their keys. ```go title="Signature" func (r *Request) DelPathParams(key ...string) *Request ``` ### ResetPathParams **ResetPathParams** deletes all path parameters. ```go title="Signature" func (r *Request) ResetPathParams() *Request ``` ## SetJSON **SetJSON** sets the request body to a JSON-encoded payload. ```go title="Signature" func (r *Request) SetJSON(v any) *Request ``` ## SetXML **SetXML** sets the request body to an XML-encoded payload. ```go title="Signature" func (r *Request) SetXML(v any) *Request ``` ## SetCBOR **SetCBOR** sets the request body to a CBOR-encoded payload. It automatically sets the `Content-Type` to `application/cbor`. ```go title="Signature" func (r *Request) SetCBOR(v any) *Request ``` ## SetRawBody **SetRawBody** sets the request body to raw bytes. ```go title="Signature" func (r *Request) SetRawBody(v []byte) *Request ``` ## FormData **FormData** returns all values associated with the given form data field. ```go title="Signature" func (r *Request) FormData(key string) []string ``` ### AllFormData **AllFormData** returns an iterator over all form data fields. Use `maps.Collect()` if needed. ```go title="Signature" func (r *Request) AllFormData() iter.Seq2[string, []string] ``` ### AddFormData **AddFormData** adds a single form data key-value pair. ```go title="Signature" func (r *Request) AddFormData(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.AddFormData("points", "80") req.AddFormData("points", "90") req.AddFormData("points", "100") resp, err := req.Post("https://httpbin.org/post") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "args": {}, "data": "", "files": {}, "form": { "points": [ "80", "90", "100" ] }, // ... } ```
### SetFormData **SetFormData** sets a single form data field, overriding any previously set values. ```go title="Signature" func (r *Request) SetFormData(key, val string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetFormData("name", "john") req.SetFormData("email", "john@doe.com") resp, err := req.Post("https://httpbin.org/post") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "args": {}, "data": "", "files": {}, "form": { "email": "john@doe.com", "name": "john" }, // ... } ```
### AddFormDataWithMap **AddFormDataWithMap** adds multiple form data fields and values from a map of string slices. ```go title="Signature" func (r *Request) AddFormDataWithMap(m map[string][]string) *Request ``` ### SetFormDataWithMap **SetFormDataWithMap** sets multiple form data fields from a map of strings. ```go title="Signature" func (r *Request) SetFormDataWithMap(m map[string]string) *Request ``` ### SetFormDataWithStruct **SetFormDataWithStruct** sets multiple form data fields from a struct. ```go title="Signature" func (r *Request) SetFormDataWithStruct(v any) *Request ``` ### DelFormData **DelFormData** deletes one or more form data fields by their keys. ```go title="Signature" func (r *Request) DelFormData(key ...string) *Request ``` ## File **File** returns a file from the request by its name. If no name was provided, it attempts to match by path. ```go title="Signature" func (r *Request) File(name string) *File ``` ### Files **Files** returns all files in the request as a slice. The returned slice is valid only until the request is released. ```go title="Signature" func (r *Request) Files() []*File ``` ### FileByPath **FileByPath** returns a file from the request by its file path. ```go title="Signature" func (r *Request) FileByPath(path string) *File ``` ### AddFile **AddFile** adds a single file to the request from a file path. ```go title="Signature" func (r *Request) AddFile(path string) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.AddFile("test.txt") resp, err := req.Post("https://httpbin.org/post") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "args": {}, "data": "", "files": { "file1": "This is an empty file!\n" }, "form": {}, // ... } ```
### AddFileWithReader **AddFileWithReader** adds a single file to the request from an `io.ReadCloser`. ```go title="Signature" func (r *Request) AddFileWithReader(name string, reader io.ReadCloser) *Request ```
Example ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) buf := bytes.NewBuffer([]byte("Hello, World!")) req.AddFileWithReader("test.txt", io.NopCloser(buf)) resp, err := req.Post("https://httpbin.org/post") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "args": {}, "data": "", "files": { "file1": "Hello, World!" }, "form": {}, // ... } ```
### AddFiles **AddFiles** adds multiple files to the request at once. ```go title="Signature" func (r *Request) AddFiles(files ...*File) *Request ``` ## Timeout **Timeout** returns the timeout duration set in the request. ```go title="Signature" func (r *Request) Timeout() time.Duration ``` ## SetTimeout **SetTimeout** sets a timeout for the request, overriding any timeout set at the client level. ```go title="Signature" func (r *Request) SetTimeout(t time.Duration) *Request ```
Example 1 ```go title="Example 1" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetTimeout(5 * time.Second) resp, err := req.Get("https://httpbin.org/delay/4") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```json { "args": {}, "data": "", "files": {}, "form": {}, // ... } ```
Example 2 ```go title="Example 2" req := client.AcquireRequest() defer client.ReleaseRequest(req) req.SetTimeout(5 * time.Second) resp, err := req.Get("https://httpbin.org/delay/6") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ``` ```shell panic: timeout or cancel goroutine 1 [running]: main.main() main.go:18 +0xeb exit status 2 ```
## MaxRedirects **MaxRedirects** returns the maximum number of redirects allowed for the request. ```go title="Signature" func (r *Request) MaxRedirects() int ``` ## SetMaxRedirects **SetMaxRedirects** sets the maximum number of redirects for the request, overriding the client's setting. ```go title="Signature" func (r *Request) SetMaxRedirects(count int) *Request ``` ## Send **Send** executes the HTTP request and returns a `Response`. ```go title="Signature" func (r *Request) Send() (*Response, error) ``` ## Reset **Reset** clears the `Request` object, making it ready for reuse. This is used by `ReleaseRequest`. ```go title="Signature" func (r *Request) Reset() ``` ## Header **Header** is a wrapper around `fasthttp.RequestHeader`, storing headers for both the client and request. ```go type Header struct { *fasthttp.RequestHeader } ``` ### PeekMultiple **PeekMultiple** returns multiple values associated with the same header key. ```go title="Signature" func (h *Header) PeekMultiple(key string) []string ``` ### AddHeaders **AddHeaders** adds multiple headers from a map of string slices. ```go title="Signature" func (h *Header) AddHeaders(r map[string][]string) ``` ### SetHeaders **SetHeaders** sets multiple headers from a map of strings, overriding previously set headers. ```go title="Signature" func (h *Header) SetHeaders(r map[string]string) ``` ## QueryParam **QueryParam** is a wrapper around `fasthttp.Args`, storing query parameters. ```go type QueryParam struct { *fasthttp.Args } ``` ### Keys **Keys** returns all keys in the query parameters. ```go title="Signature" func (p *QueryParam) Keys() []string ``` ### AddParams **AddParams** adds multiple query parameters from a map of string slices. ```go title="Signature" func (p *QueryParam) AddParams(r map[string][]string) ``` ### SetParams **SetParams** sets multiple query parameters from a map of strings, overriding previously set values. ```go title="Signature" func (p *QueryParam) SetParams(r map[string]string) ``` ### SetParamsWithStruct **SetParamsWithStruct** sets multiple query parameters from a struct. Nested structs are not supported. ```go title="Signature" func (p *QueryParam) SetParamsWithStruct(v any) ``` ## Cookie **Cookie** is a map that stores cookies. ```go type Cookie map[string]string ``` ### Add **Add** adds a cookie key-value pair. ```go title="Signature" func (c Cookie) Add(key, val string) ``` ### Del **Del** removes a cookie by its key. ```go title="Signature" func (c Cookie) Del(key string) ``` ### SetCookie **SetCookie** sets a single cookie key-value pair, overriding previously set values. ```go title="Signature" func (c Cookie) SetCookie(key, val string) ``` ### SetCookies **SetCookies** sets multiple cookies from a map of strings. ```go title="Signature" func (c Cookie) SetCookies(m map[string]string) ``` ### SetCookiesWithStruct **SetCookiesWithStruct** sets multiple cookies from a struct. ```go title="Signature" func (c Cookie) SetCookiesWithStruct(v any) ``` ### DelCookies **DelCookies** deletes one or more cookies by their keys. ```go title="Signature" func (c Cookie) DelCookies(key ...string) ``` ### All **All** returns an iterator over all cookies. The key and value returned should not be retained after the loop ends. ```go title="Signature" func (c Cookie) All() iter.Seq2[string, string] ``` ### Reset **Reset** clears all cookies. ```go title="Signature" func (c Cookie) Reset() ``` ## PathParam **PathParam** is a map that stores path parameters. ```go type PathParam map[string]string ``` ### Add **Add** adds a path parameter key-value pair. ```go title="Signature" func (p PathParam) Add(key, val string) ``` ### Del **Del** removes a path parameter by its key. ```go title="Signature" func (p PathParam) Del(key string) ``` ### SetParam **SetParam** sets a single path parameter key-value pair, overriding previously set values. ```go title="Signature" func (p PathParam) SetParam(key, val string) ``` ### SetParams **SetParams** sets multiple path parameters from a map of strings. ```go title="Signature" func (p PathParam) SetParams(m map[string]string) ``` ### SetParamsWithStruct **SetParamsWithStruct** sets multiple path parameters from a struct. ```go title="Signature" func (p PathParam) SetParamsWithStruct(v any) ``` ### DelParams **DelParams** deletes one or more path parameters by their keys. ```go title="Signature" func (p PathParam) DelParams(key ...string) ``` ### All **All** returns an iterator over all path parameters. The key and value returned should not be retained after the loop ends. ```go title="Signature" func (p PathParam) All() iter.Seq2[string, string] ``` ### Reset **Reset** clears all path parameters. ```go title="Signature" func (p PathParam) Reset() ``` ## FormData **FormData** is a wrapper around `fasthttp.Args`, used to handle URL-encoded and form-data (multipart) request bodies. ```go type FormData struct { *fasthttp.Args } ``` ### Keys **Keys** returns all form data keys. ```go title="Signature" func (f *FormData) Keys() []string ``` ### Add **Add** adds a single form field key-value pair. ```go title="Signature" func (f *FormData) Add(key, val string) ``` ### Set **Set** sets a single form field key-value pair, overriding any previously set values. ```go title="Signature" func (f *FormData) Set(key, val string) ``` ### AddWithMap **AddWithMap** adds multiple form fields from a map of string slices. ```go title="Signature" func (f *FormData) AddWithMap(m map[string][]string) ``` ### SetWithMap **SetWithMap** sets multiple form fields from a map of strings. ```go title="Signature" func (f *FormData) SetWithMap(m map[string]string) ``` ### SetWithStruct **SetWithStruct** sets multiple form fields from a struct. ```go title="Signature" func (f *FormData) SetWithStruct(v any) ``` ### DelData **DelData** deletes one or more form fields by their keys. ```go title="Signature" func (f *FormData) DelData(key ...string) ``` ### Reset **Reset** clears all form data fields. ```go title="Signature" func (f *FormData) Reset() ``` ## File **File** represents a file to be uploaded. It can be specified by name, path, or an `io.ReadCloser`. ```go type File struct { name string fieldName string path string reader io.ReadCloser } ``` ### AcquireFile **AcquireFile** returns a `File` from the pool and applies any provided `SetFileFunc` functions to it. Release it with `ReleaseFile` when done. ```go title="Signature" func AcquireFile(setter ...SetFileFunc) *File ``` ### ReleaseFile **ReleaseFile** returns the `File` to the pool. Do not use the file afterward. ```go title="Signature" func ReleaseFile(f *File) ``` ### SetName **SetName** sets the file's name. ```go title="Signature" func (f *File) SetName(n string) ``` ### SetFieldName **SetFieldName** sets the field name of the file in the multipart form. ```go title="Signature" func (f *File) SetFieldName(n string) ``` ### SetPath **SetPath** sets the file's path. ```go title="Signature" func (f *File) SetPath(p string) ``` ### SetReader **SetReader** sets the file's `io.ReadCloser`. The reader is closed automatically when the request body is parsed. ```go title="Signature" func (f *File) SetReader(r io.ReadCloser) ``` ### Reset **Reset** clears the file's fields. ```go title="Signature" func (f *File) Reset() ``` --- ## 📥 Response The `Response` struct in Fiber's HTTP client represents the server's reply and exposes: - **Status Code**: The HTTP status code returned by the server (e.g., `200 OK`, `404 Not Found`). - **Headers**: All HTTP headers returned by the server, providing additional response-related information. - **Body**: The response body content, which can be JSON, XML, plain text, or other formats. - **Cookies**: Any cookies the server sent along with the response. It makes it easy to inspect and handle data returned by the server. ```go type Response struct { client *Client request *Request cookie []*fasthttp.Cookie RawResponse *fasthttp.Response } ``` Use the index to jump straight to any response method; filter by name: ## AcquireResponse **AcquireResponse** returns a new pooled `Response`. Call `ReleaseResponse` when you're done to return it to the pool and limit allocations. ```go title="Signature" func AcquireResponse() *Response ``` ## ReleaseResponse **ReleaseResponse** puts the `Response` back into the pool. Do not use it after releasing; doing so can trigger data races. ```go title="Signature" func ReleaseResponse(resp *Response) ``` ## Status **Status** returns the HTTP status message (e.g., `OK`, `Not Found`) associated with the response. ```go title="Signature" func (r *Response) Status() string ``` ## StatusCode **StatusCode** returns the numeric HTTP status code of the response. ```go title="Signature" func (r *Response) StatusCode() int ``` ## Protocol **Protocol** returns the HTTP protocol used (e.g., `HTTP/1.1`, `HTTP/2`) for the response. ```go title="Signature" func (r *Response) Protocol() string ```
Example ```go title="Example" resp, err := client.Get("https://httpbin.org/get") if err != nil { panic(err) } fmt.Println(resp.Protocol()) ``` **Output:** ```text HTTP/1.1 ```
## Header **Header** retrieves the value of a specific response header by key. If multiple values exist for the same header, this returns the first one. ```go title="Signature" func (r *Response) Header(key string) string ``` ## Headers **Headers** returns an iterator over all response headers. Use `maps.Collect()` to convert them into a map if desired. The returned values are only valid until the response is released, so make copies if needed. ```go title="Signature" func (r *Response) Headers() iter.Seq2[string, []string] ```
Example ```go title="Example" resp, err := client.Get("https://httpbin.org/get") if err != nil { panic(err) } for key, values := range resp.Headers() { fmt.Printf("%s => %s\n", key, strings.Join(values, ", ")) } ``` **Output:** ```text Date => Wed, 04 Dec 2024 15:28:29 GMT Connection => keep-alive Access-Control-Allow-Origin => * Access-Control-Allow-Credentials => true ```
Example with maps.Collect() ```go title="Example with maps.Collect()" resp, err := client.Get("https://httpbin.org/get") if err != nil { panic(err) } headers := maps.Collect(resp.Headers()) for key, values := range headers { fmt.Printf("%s => %s\n", key, strings.Join(values, ", ")) } ``` **Output:** ```text Date => Wed, 04 Dec 2024 15:28:29 GMT Connection => keep-alive Access-Control-Allow-Origin => * Access-Control-Allow-Credentials => true ```
## Cookies **Cookies** returns a slice of all cookies set by the server in this response. The slice is only valid until the response is released. ```go title="Signature" func (r *Response) Cookies() []*fasthttp.Cookie ```
Example ```go title="Example" resp, err := client.Get("https://httpbin.org/cookies/set/go/fiber") if err != nil { panic(err) } cookies := resp.Cookies() for _, cookie := range cookies { fmt.Printf("%s => %s\n", string(cookie.Key()), string(cookie.Value())) } ``` **Output:** ```text go => fiber ```
## Body **Body** returns the raw response body as a byte slice. ```go title="Signature" func (r *Response) Body() []byte ``` ## BodyStream **BodyStream** returns the response body as an `io.Reader`, allowing incremental reading without loading the entire body into memory. This is particularly useful when `Client.SetStreamResponseBody(true)` is enabled. When streaming is enabled, the underlying stream from fasthttp is returned directly. When streaming is not enabled, a `bytes.Reader` wrapping the body is returned as a fallback. :::note When using `BodyStream()`, the response body is consumed as you read. Calling `Body()` afterward may return an empty slice if the stream has been fully read. ::: ```go title="Signature" func (r *Response) BodyStream() io.Reader ```
Example ```go title="Example" cc := client.New() cc.SetStreamResponseBody(true) resp, err := cc.Get("https://httpbin.org/bytes/1024") if err != nil { panic(err) } defer resp.Close() buf := make([]byte, 256) total, err := io.CopyBuffer(io.Discard, resp.BodyStream(), buf) if err != nil { panic(err) } fmt.Printf("Read %d bytes\n", total) ``` **Output:** ```text Read 1024 bytes ```
## IsStreaming **IsStreaming** returns `true` if the response body is being streamed (i.e., when `Client.SetStreamResponseBody(true)` was set and the underlying transport provided a stream). ```go title="Signature" func (r *Response) IsStreaming() bool ```
Example ```go title="Example" cc := client.New() cc.SetStreamResponseBody(true) resp, err := cc.Get("https://httpbin.org/get") if err != nil { panic(err) } defer resp.Close() if resp.IsStreaming() { fmt.Println("Response is streaming") // Use resp.BodyStream() to read incrementally } else { fmt.Println("Response is buffered") // Use resp.Body() for direct access } ```
## String **String** returns the response body as a trimmed string. ```go title="Signature" func (r *Response) String() string ``` ## JSON **JSON** unmarshal the response body into the provided variable `v` using JSON. `v` should be a pointer to a struct or a type compatible with JSON unmarshal. ```go title="Signature" func (r *Response) JSON(v any) error ```
Example ```go title="Example" type Body struct { Slideshow struct { Author string `json:"author"` Date string `json:"date"` Title string `json:"title"` } `json:"slideshow"` } var out Body resp, err := client.Get("https://httpbin.org/json") if err != nil { panic(err) } if err = resp.JSON(&out); err != nil { panic(err) } fmt.Printf("%+v\n", out) ``` **Output:** ```text {Slideshow:{Author:Yours Truly Date:date of publication Title:Sample Slide Show}} ```
## XML **XML** unmarshal the response body into the provided variable `v` using XML decoding. ```go title="Signature" func (r *Response) XML(v any) error ``` ## CBOR **CBOR** unmarshal the response body into `v` using CBOR decoding. ```go title="Signature" func (r *Response) CBOR(v any) error ``` ## Save **Save** writes the response body to a file or an `io.Writer`. If `v` is a string, it interprets it as a file path, creates the file (and directories if needed), and writes the response to it. If `v` is an `io.Writer`, it writes directly to it. ```go title="Signature" func (r *Response) Save(v any) error ``` ## Reset **Reset** clears the `Response` object, making it ready for reuse by `ReleaseResponse`. ```go title="Signature" func (r *Response) Reset() ``` ## Close **Close** releases both the associated `Request` and `Response` objects back to their pools. :::warning After calling `Close`, any attempt to use the request or response may result in data races or undefined behavior. Ensure all processing is complete before closing. ::: ```go title="Signature" func (r *Response) Close() ``` --- ## 🖥️ REST The Fiber Client is a high-performance HTTP client built on FastHTTP. It handles both internal service calls and external requests with minimal overhead. Use the index to jump straight to any client method; filter by name or by category: ## Features - **Lightweight and fast**: built on FastHTTP for minimal overhead. - **Flexible configuration**: set global defaults like timeouts or headers and override them per request. - **Connection pooling**: reuses persistent connections instead of opening new ones. - **Timeouts and retries**: supports per-request deadlines and retry policies for transient errors. ## Usage Create a client with any required configuration, then send requests: ```go package main import ( "fmt" "time" "github.com/gofiber/fiber/v3/client" ) func main() { cc := client.New() cc.SetTimeout(10 * time.Second) // Send a GET request resp, err := cc.Get("https://httpbin.org/get") if err != nil { panic(err) } fmt.Printf("Status: %d\n", resp.StatusCode()) fmt.Printf("Body: %s\n", string(resp.Body())) } ``` See [examples](examples.md) for more detailed usage. ```go type Client struct { logger log.CommonLogger transport httpClientTransport header *Header params *QueryParam cookies *Cookie path *PathParam jsonMarshal utils.JSONMarshal jsonUnmarshal utils.JSONUnmarshal xmlMarshal utils.XMLMarshal xmlUnmarshal utils.XMLUnmarshal cborMarshal utils.CBORMarshal cborUnmarshal utils.CBORUnmarshal cookieJar *CookieJar retryConfig *RetryConfig baseURL string userAgent string referer string userRequestHooks []RequestHook builtinRequestHooks []RequestHook userResponseHooks []ResponseHook builtinResponseHooks []ResponseHook timeout time.Duration mu sync.RWMutex isDebug bool isPathNormalizingDisabled bool } ``` ### New **New** creates and returns a new Client object. ```go title="Signature" func New() *Client ``` ### NewWithClient **NewWithClient** creates and returns a new Client object from an existing `fasthttp.Client`. ```go title="Signature" func NewWithClient(c *fasthttp.Client) *Client ``` ## REST Methods These helpers mirror axios-style method names and send HTTP requests using the configured client: ### Get Sends a GET request. ```go title="Signature" func (c *Client) Get(url string, cfg ...Config) (*Response, error) ``` ### Post Sends a POST request. ```go title="Signature" func (c *Client) Post(url string, cfg ...Config) (*Response, error) ``` ### Put Sends a PUT request. ```go title="Signature" func (c *Client) Put(url string, cfg ...Config) (*Response, error) ``` ### Patch Sends a PATCH request. ```go title="Signature" func (c *Client) Patch(url string, cfg ...Config) (*Response, error) ``` ### Query Sends a QUERY request. ```go title="Signature" func (c *Client) Query(url string, cfg ...Config) (*Response, error) ``` ### Delete Sends a DELETE request. ```go title="Signature" func (c *Client) Delete(url string, cfg ...Config) (*Response, error) ``` ### Head Sends a HEAD request. ```go title="Signature" func (c *Client) Head(url string, cfg ...Config) (*Response, error) ``` ### Options Sends an OPTIONS request. ```go title="Signature" func (c *Client) Options(url string, cfg ...Config) (*Response, error) ``` ### Custom Sends a request with any HTTP method. ```go title="Signature" func (c *Client) Custom(url, method string, cfg ...Config) (*Response, error) ``` ## Request Configuration The `Config` type holds per-request parameters. JSON is used to serialize the body by default. If multiple body sources are set, precedence is: 1. Body 2. FormData 3. File ```go type Config struct { Ctx context.Context UserAgent string Referer string Header map[string]string Param map[string]string Cookie map[string]string PathParam map[string]string Timeout time.Duration MaxRedirects int Body any FormData map[string]string File []*File } ``` ## R **R** gets a `Request` object from the pool. Call `ReleaseRequest` when finished. ```go title="Signature" func (c *Client) R() *Request ``` ## Hooks Hooks allow you to add custom logic before a request is sent or after a response is received. ### RequestHook **RequestHook** returns user-defined request hooks. ```go title="Signature" func (c *Client) RequestHook() []RequestHook ``` ### ResponseHook **ResponseHook** returns user-defined response hooks. ```go title="Signature" func (c *Client) ResponseHook() []ResponseHook ``` ### AddRequestHook Adds one or more user-defined request hooks. ```go title="Signature" func (c *Client) AddRequestHook(h ...RequestHook) *Client ``` ### AddResponseHook Adds one or more user-defined response hooks. ```go title="Signature" func (c *Client) AddResponseHook(h ...ResponseHook) *Client ``` ## JSON ### JSONMarshal Returns the JSON marshaler function used by the client. ```go title="Signature" func (c *Client) JSONMarshal() utils.JSONMarshal ``` ### JSONUnmarshal Returns the JSON unmarshaler function used by the client. ```go title="Signature" func (c *Client) JSONUnmarshal() utils.JSONUnmarshal ``` ### SetJSONMarshal Sets a custom JSON marshaler. ```go title="Signature" func (c *Client) SetJSONMarshal(f utils.JSONMarshal) *Client ``` ### SetJSONUnmarshal Sets a custom JSON unmarshaler. ```go title="Signature" func (c *Client) SetJSONUnmarshal(f utils.JSONUnmarshal) *Client ``` ## XML ### XMLMarshal Returns the XML marshaler function used by the client. ```go title="Signature" func (c *Client) XMLMarshal() utils.XMLMarshal ``` ### XMLUnmarshal Returns the XML unmarshaler function used by the client. ```go title="Signature" func (c *Client) XMLUnmarshal() utils.XMLUnmarshal ``` ### SetXMLMarshal Sets a custom XML marshaler. ```go title="Signature" func (c *Client) SetXMLMarshal(f utils.XMLMarshal) *Client ``` ### SetXMLUnmarshal Sets a custom XML unmarshaler. ```go title="Signature" func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client ``` ## CBOR ### CBORMarshal Returns the CBOR marshaler function used by the client. ```go title="Signature" func (c *Client) CBORMarshal() utils.CBORMarshal ``` ### CBORUnmarshal Returns the CBOR unmarshaler function used by the client. ```go title="Signature" func (c *Client) CBORUnmarshal() utils.CBORUnmarshal ``` ### SetCBORMarshal Sets a custom CBOR marshaler. ```go title="Signature" func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client ``` ### SetCBORUnmarshal Sets a custom CBOR unmarshaler. ```go title="Signature" func (c *Client) SetCBORUnmarshal(f utils.CBORUnmarshal) *Client ``` ## TLS ### TLSConfig Returns the client's TLS configuration. If none is set, it initializes a new configuration with `MinVersion` defaulting to TLS 1.2. ```go title="Signature" func (c *Client) TLSConfig() *tls.Config ``` ### SetTLSConfig Sets the TLS configuration for the client. ```go title="Signature" func (c *Client) SetTLSConfig(config *tls.Config) *Client ``` ### SetCertificates Adds client certificates to the TLS configuration. ```go title="Signature" func (c *Client) SetCertificates(certs ...tls.Certificate) *Client ``` ### SetRootCertificate Adds one or more root certificates to the client's trust store. ```go title="Signature" func (c *Client) SetRootCertificate(path string) *Client ``` ### SetRootCertificateFromString Adds one or more root certificates from a string. ```go title="Signature" func (c *Client) SetRootCertificateFromString(pem string) *Client ``` ## SetProxyURL Sets a proxy URL for the client. All subsequent requests will use this proxy. ```go title="Signature" func (c *Client) SetProxyURL(proxyURL string) error ``` ## Response Streaming ### StreamResponseBody Returns whether response body streaming is enabled. When enabled, the response body is not fully loaded into memory and can be read as a stream using `Response.BodyStream()`. This is useful for handling large responses or server-sent events (SSE). ```go title="Signature" func (c *Client) StreamResponseBody() bool ``` ### SetStreamResponseBody Enables or disables response body streaming. When enabled, responses can be consumed incrementally without loading the entire body into memory. ```go title="Signature" func (c *Client) SetStreamResponseBody(enable bool) *Client ``` **Example:** ```go title="Example" cc := client.New() cc.SetStreamResponseBody(true) resp, err := cc.Get("https://example.com/large-file") if err != nil { panic(err) } defer resp.Close() // Check if response is streaming if resp.IsStreaming() { // Read body incrementally reader := resp.BodyStream() buf := make([]byte, 4096) for { n, err := reader.Read(buf) if n > 0 { // Process chunk... } if err == io.EOF { break } if err != nil { panic(err) } } } else { // Regular body access body := resp.Body() fmt.Println(string(body)) } ``` **Server-Sent Events Example:** ```go title="SSE Example" cc := client.New() cc.SetStreamResponseBody(true) resp, err := cc.Get("https://example.com/events") if err != nil { panic(err) } defer resp.Close() reader := bufio.NewReader(resp.BodyStream()) for { line, err := reader.ReadString('\n') if err == io.EOF { break } if err != nil { panic(err) } fmt.Print(line) // Process SSE event } ``` ## RetryConfig Returns the retry configuration of the client. ```go title="Signature" func (c *Client) RetryConfig() *RetryConfig ``` ## SetRetryConfig Sets the retry configuration for the client. ```go title="Signature" func (c *Client) SetRetryConfig(config *RetryConfig) *Client ``` ## BaseURL ### BaseURL **BaseURL** returns the base URL currently set in the client. ```go title="Signature" func (c *Client) BaseURL() string ``` ### SetBaseURL Sets a base URL prefix for all requests made by the client. ```go title="Signature" func (c *Client) SetBaseURL(url string) *Client ``` **Example:** ```go title="Example" cc := client.New() cc.SetBaseURL("https://httpbin.org/") resp, err := cc.Get("/get") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ```
Click here to see the result ```json { "args": {}, ... } ```
## Headers ### Header Retrieves all values of a header key at the client level. The returned values apply to all requests. ```go title="Signature" func (c *Client) Header(key string) []string ``` ### AddHeader Adds a single header to all requests initiated by this client. ```go title="Signature" func (c *Client) AddHeader(key, val string) *Client ``` ### SetHeader Sets a single header, overriding any existing headers with the same key. ```go title="Signature" func (c *Client) SetHeader(key, val string) *Client ``` ### AddHeaders Adds multiple headers at once, all applying to all future requests from this client. ```go title="Signature" func (c *Client) AddHeaders(h map[string][]string) *Client ``` ### SetHeaders Sets multiple headers at once, overriding previously set headers. ```go title="Signature" func (c *Client) SetHeaders(h map[string]string) *Client ``` ## Query Parameters ### Param Returns the values for a given query parameter key. ```go title="Signature" func (c *Client) Param(key string) []string ``` ### AddParam Adds a single query parameter for all requests. ```go title="Signature" func (c *Client) AddParam(key, val string) *Client ``` ### SetParam Sets a single query parameter, overriding previously set values. ```go title="Signature" func (c *Client) SetParam(key, val string) *Client ``` ### AddParams Adds multiple query parameters from a map of string slices. ```go title="Signature" func (c *Client) AddParams(m map[string][]string) *Client ``` ### SetParams Sets multiple query parameters from a map, overriding previously set values. ```go title="Signature" func (c *Client) SetParams(m map[string]string) *Client ``` ### SetParamsWithStruct Sets multiple query parameters from a struct. Nested structs are not currently supported. ```go title="Signature" func (c *Client) SetParamsWithStruct(v any) *Client ``` ### DelParams Deletes one or more query parameters. ```go title="Signature" func (c *Client) DelParams(key ...string) *Client ``` ## UserAgent & Referer ### SetUserAgent Sets the user agent header for all requests. ```go title="Signature" func (c *Client) SetUserAgent(ua string) *Client ``` ### SetReferer Sets the referer header for all requests. ```go title="Signature" func (c *Client) SetReferer(r string) *Client ``` ## Path Parameters ### PathParam Returns the value of a named path parameter, if set. ```go title="Signature" func (c *Client) PathParam(key string) string ``` ### SetPathParam Sets a single path parameter. ```go title="Signature" func (c *Client) SetPathParam(key, val string) *Client ``` ### SetPathParams Sets multiple path parameters at once. ```go title="Signature" func (c *Client) SetPathParams(m map[string]string) *Client ``` ### SetPathParamsWithStruct Sets multiple path parameters from a struct. ```go title="Signature" func (c *Client) SetPathParamsWithStruct(v any) *Client ``` ### DelPathParams Deletes one or more path parameters. ```go title="Signature" func (c *Client) DelPathParams(key ...string) *Client ``` ## Cookies ### Cookie Returns the value of a named cookie if set at the client level. ```go title="Signature" func (c *Client) Cookie(key string) string ``` ### SetCookie Sets a single cookie for all requests. ```go title="Signature" func (c *Client) SetCookie(key, val string) *Client ``` **Example:** ```go title="Example" cc := client.New() cc.SetCookie("john", "doe") resp, err := cc.Get("https://httpbin.org/cookies") if err != nil { panic(err) } fmt.Println(string(resp.Body())) ```
Click here to see the result ```json { "cookies": { "john": "doe" } } ```
### SetCookies Sets multiple cookies at once. ```go title="Signature" func (c *Client) SetCookies(m map[string]string) *Client ``` ### SetCookiesWithStruct Sets multiple cookies from a struct. ```go title="Signature" func (c *Client) SetCookiesWithStruct(v any) *Client ``` ### DelCookies Deletes one or more cookies. ```go title="Signature" func (c *Client) DelCookies(key ...string) *Client ``` ## Timeout ### SetTimeout Sets a default timeout for all requests, which can be overridden per request. ```go title="Signature" func (c *Client) SetTimeout(t time.Duration) *Client ``` ## Debugging ### Debug Enables debug-level logging output. ```go title="Signature" func (c *Client) Debug() *Client ``` ### DisableDebug Disables debug-level logging output. ```go title="Signature" func (c *Client) DisableDebug() *Client ``` ## Cookie Jar ### SetCookieJar Assigns a cookie jar to the client to store and manage cookies across requests. ```go title="Signature" func (c *Client) SetCookieJar(cookieJar *CookieJar) *Client ``` ## Dial & Logger ### SetDial Sets a custom dial function. ```go title="Signature" func (c *Client) SetDial(dial fasthttp.DialFunc) *Client ``` ### SetLogger Sets the logger instance used by the client. ```go title="Signature" func (c *Client) SetLogger(logger log.CommonLogger) *Client ``` ### Logger Returns the current logger instance. ```go title="Signature" func (c *Client) Logger() log.CommonLogger ``` ## Reset ### Reset Clears and resets the client to its default state and reinstates the default `fasthttp.Client` transport. ```go title="Signature" func (c *Client) Reset() ``` ## Default Client Fiber provides a default client (created with `New()`). You can configure it or replace it as needed. ### C **C** returns the default client. ```go title="Signature" func C() *Client ``` ### Get Get is a convenience method that sends a GET request using the `defaultClient`. ```go title="Signature" func Get(url string, cfg ...Config) (*Response, error) ``` ### Post Post is a convenience method that sends a POST request using the `defaultClient`. ```go title="Signature" func Post(url string, cfg ...Config) (*Response, error) ``` ### Put Put is a convenience method that sends a PUT request using the `defaultClient`. ```go title="Signature" func Put(url string, cfg ...Config) (*Response, error) ``` ### Patch Patch is a convenience method that sends a PATCH request using the `defaultClient`. ```go title="Signature" func Patch(url string, cfg ...Config) (*Response, error) ``` ### Query Query is a convenience method that sends a QUERY request using the `defaultClient`. ```go title="Signature" func Query(url string, cfg ...Config) (*Response, error) ``` ### Delete Delete is a convenience method that sends a DELETE request using the `defaultClient`. ```go title="Signature" func Delete(url string, cfg ...Config) (*Response, error) ``` ### Head Head sends a HEAD request using the `defaultClient`, a convenience method. ```go title="Signature" func Head(url string, cfg ...Config) (*Response, error) ``` ### Options Options is a convenience method that sends an OPTIONS request using the `defaultClient`. ```go title="Signature" func Options(url string, cfg ...Config) (*Response, error) ``` ### Replace **Replace** replaces the default client with a new one. It returns a function that can restore the old client. :::caution Replacing the default client is concurrency-safe, but mutating the same `Client` instance still requires external synchronization. ::: ```go title="Signature" func Replace(c *Client) func() ``` --- ## 🗺️ Ecosystem Fiber is a family of repositories around one core module. The map below shows what lives where and how the pieces plug into each other; select any block for details and the code that wires it up: --- ## 📊 Benchmarks ## TechEmpower [TechEmpower](https://www.techempower.com/benchmarks/#section=test&runid=1d5bfc8a-5c4a-4fb2-a792-ad967f1eb138) provides a performance comparison of many web application frameworks that execute fundamental tasks such as JSON serialization, database access, and server-side template rendering. Each framework runs under a realistic production configuration. Results are recorded on both cloud instances and physical hardware. The test implementations are community contributed and live in the [FrameworkBenchmarks repository](https://github.com/TechEmpower/FrameworkBenchmarks). * Fiber `v3.0.0` * 56 Cores Intel(R) Xeon(R) Gold 6330 CPU @ 2.00GHz (Three homogeneous ProLiant DL360 Gen10 Plus) * 64GB RAM * Enterprise SSD * Ubuntu * Mellanox Technologies MT28908 Family ConnectX-6 40Gbps Ethernet ### Plaintext The Plaintext test measures basic request routing and demonstrates the capacity of high-performance platforms. Requests are pipelined, and the tiny response body demands high throughput to saturate the benchmark's gigabit Ethernet. See [Plaintext requirements](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#plaintext) **Fiber** - **11,987,976** responses per second with an average latency of **1.0** ms. **Express** - **1,204,969** responses per second with an average latency of **8.8** ms. ![](/img/v3/plaintext.png) ![Fiber vs Express](/img/v3/plaintext_express.png) ### Data Updates **Fiber** handled **29,984** responses per second with an average latency of **16.9** ms. **Express** handled **54,887** responses per second with an average latency of **9.2** ms. ![](/img/v3/data_updates.png) ![Fiber vs Express](/img/v3/data_updates_express.png) ### Multiple Queries **Fiber** handled **54,002** responses per second with an average latency of **9.4** ms. **Express** handled **85,011** responses per second with an average latency of **6.0** ms. ![](/img/v3/multiple_queries.png) ![Fiber vs Express](/img/v3/multiple_queries_express.png) ### Single Query **Fiber** handled **953,016** responses per second with an average latency of **0.6** ms. **Express** handled **441,543** responses per second with an average latency of **1.3** ms. ![](/img/v3/single_query.png) ![Fiber vs Express](/img/v3/single_query_express.png) ### JSON Serialization **Fiber** handled **2,363,294** responses per second with an average latency of **0.2** ms. **Express** handled **949,717** responses per second with an average latency of **0.5** ms. ![](/img/v3/json.png) ![Fiber vs Express](/img/v3/json_express.png) --- ## 🤔 FAQ ## How should I structure my application? There's no single answer; the ideal structure depends on your application's scale and team. Fiber makes no assumptions about project layout. Routes and other application logic can live in any files or directories. For inspiration, see: * [gofiber/boilerplate](https://github.com/gofiber/boilerplate) * [thomasvvugt/fiber-boilerplate](https://github.com/thomasvvugt/fiber-boilerplate) * [Youtube - Building a REST API using Gorm and Fiber](https://www.youtube.com/watch?v=Iq2qT0fRhAA) * [embedmode/fiberseed](https://github.com/embedmode/fiberseed) ## How do I handle custom 404 responses? If you're using v2.32.0 or later, implement a custom error handler as shown below or read more at [Error Handling](../guide/error-handling.md#custom-error-handler). If you're using v2.31.0 or earlier, the error handler will not capture 404 errors. Instead, add a middleware function at the very bottom of the stack \(below all other functions\) to handle a 404 response: ```go title="Example" app.Use(func(c fiber.Ctx) error { return c.Status(fiber.StatusNotFound).SendString("Sorry can't find that!") }) ``` ## How can I use live reload? [Air](https://github.com/air-verse/air) automatically restarts your Go application when source files change, speeding development. To use Air in a Fiber project, follow these steps: * Install Air by downloading the appropriate binary for your operating system from the GitHub release page or by building the tool from source. * Create a configuration file for Air in your project directory, such as `.air.toml` or `air.conf`. Here's a sample configuration file that works with Fiber: ```toml # .air.toml root = "." tmp_dir = "tmp" [build] cmd = "go build -o ./tmp/main ." bin = "./tmp/main" delay = 1000 # ms exclude_dir = ["assets", "tmp", "vendor"] include_ext = ["go", "tpl", "tmpl", "html"] exclude_regex = ["_test\\.go"] ``` * Start your Fiber application with Air by running the following command: ```sh air ``` As you edit source files, Air detects the changes and restarts the application. A complete example is available in the [Fiber Recipes repository](https://github.com/gofiber/recipes/tree/master/air) and shows how to configure Air for a Fiber project. ## How do I set up an error handler? To override the default error handler, provide a custom one in the [Config](../api/fiber.md#errorhandler) when creating a new [Fiber instance](../api/fiber.md#new). ```go title="Example" app := fiber.New(fiber.Config{ ErrorHandler: func(c fiber.Ctx, err error) error { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) }, }) ``` We have a dedicated page explaining how error handling works in Fiber, see [Error Handling](../guide/error-handling.md). ## Which template engines does Fiber support? Fiber currently supports 9 template engines in our [gofiber/template](https://docs.gofiber.io/template/) middleware: * [ace](https://docs.gofiber.io/template/ace/) * [amber](https://docs.gofiber.io/template/amber/) * [django](https://docs.gofiber.io/template/django/) * [handlebars](https://docs.gofiber.io/template/handlebars/) * [html](https://docs.gofiber.io/template/html/) * [jet](https://docs.gofiber.io/template/jet/) * [mustache](https://docs.gofiber.io/template/mustache/) * [pug](https://docs.gofiber.io/template/pug/) * [slim](https://docs.gofiber.io/template/slim/) To learn more about using Templates in Fiber, see [Templates](../guide/templates.md). ## Does Fiber have a community chat? Yes, we have a [Discord](https://gofiber.io/discord) server with rooms for every topic. If you have questions or just want to chat, join us via this [invite link](https://gofiber.io/discord). ![](/img/support-discord.png) ## Does Fiber support subdomain routing? Yes, we do. Here are some examples:
Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" ) type Host struct { Fiber *fiber.App } func main() { // Hosts hosts := map[string]*Host{} //----- // API //----- api := fiber.New() api.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) hosts["api.localhost:3000"] = &Host{api} api.Get("/", func(c fiber.Ctx) error { return c.SendString("API") }) //------ // Blog //------ blog := fiber.New() blog.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) hosts["blog.localhost:3000"] = &Host{blog} blog.Get("/", func(c fiber.Ctx) error { return c.SendString("Blog") }) //--------- // Website //--------- site := fiber.New() site.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) hosts["localhost:3000"] = &Host{site} site.Get("/", func(c fiber.Ctx) error { return c.SendString("Website") }) // Server app := fiber.New() app.Use(func(c fiber.Ctx) error { host := hosts[c.Hostname()] if host == nil { return c.SendStatus(fiber.StatusNotFound) } else { host.Fiber.Handler()(c.Context()) return nil } }) log.Fatal(app.Listen(":3000")) } ```
For more information, see issue [#750](https://github.com/gofiber/fiber/issues/750). ## How can I handle conversions between Fiber and net/http? Fiber can register common `net/http` handlers directly—just pass an `http.Handler`, `http.HandlerFunc`, compatible function, or even a native `fasthttp.RequestHandler` to your routing method. For other interoperability scenarios, the `adaptor` middleware provides utilities for converting between Fiber and `net/http`. It allows seamless integration of `net/http` handlers, middleware, and requests into Fiber applications, and vice versa. :::caution Performance trade-offs Converted `net/http` handlers run through a compatibility layer. They won't expose `fiber.Ctx` or Fiber-specific helpers, and the extra adaptation work makes them slower than native Fiber handlers. Use them when interoperability matters, but prefer Fiber handlers for maximum performance. ::: For details on how to: * Convert `net/http` handlers to Fiber handlers * Convert Fiber handlers to `net/http` handlers * Convert `fiber.Ctx` to `http.Request` See the dedicated documentation: [Adaptor Documentation](../middleware/adaptor.md). ## Troubleshooting common errors Crashes that surface in unrelated packages, with stack traces that never mention Fiber, usually trace back to a context value kept past the handler. The cases below name the symptom so a search for the panic text lands here. ### Panic `id (N) <= evictCount (M)` (hpack / HTTP/2 / gRPC) {#panic-id-evictcount-hpack-grpc} This is not a bug in Fiber, gRPC, or `golang.org/x/net/http2`. It means a Fiber zero-copy context value (a header from `c.Get`, or `c.Query`, `c.Params`, `c.Cookies`, `c.Body`) was kept past the handler. Fiber reuses the request buffer on the next request, so the retained bytes change underneath whatever stored them. See [Zero Allocation](../intro.md#zero-allocation) for the underlying behavior. A common trigger is forwarding a header into a long-lived gRPC / HTTP/2 client's `metadata`. The value is held in the connection's HPACK dynamic table and later mutates, crashing the process inside the HPACK encoder, in a stack trace that never mentions Fiber: ```text panic: id (N) <= evictCount (M) golang.org/x/net/http2/hpack.(*headerFieldTable).idToIndex ... google.golang.org/grpc/internal/transport.(*loopyWriter).writeHeader ``` The crash is decoupled from your handler in both time and call stack, so ordinary tests and even the `-race` detector usually miss it. The fix is to detach the value before you keep it: copy it with `utils.CopyString` (or `strings.Clone`), or enable [`Immutable`](../api/fiber.md#immutable). A full reproduction is in [gofiber/fiber#4464](https://github.com/gofiber/fiber/issues/4464). --- ## 🏗️ Internal Architecture ## Overall Architecture At the heart of Fiber is the **App** struct. It is responsible for configuring the server, managing a pool of Contexts (either our default implementation, **DefaultCtx**, or a user‑supplied **CustomCtx**), and holding the router stack with all registered routes and groups. In addition, the App contains mount fields to support sub‑applications and hooks that allow developers to run custom code at key stages (e.g. when registering routes or starting the server). ```mermaid flowchart TD A[App] B["Configuration (Config)"] C[Context Pool] D["DefaultCtx \/ CustomCtx"] E[Router Stack] F["Groups & Routes"] G["MountFields (Sub‑Apps)"] H[Hooks] A --> B A --> C C --> D A --> E E --> F A --> G A --> H ``` ### Explanation - App: The central object that bootstraps and runs the Fiber server. - Configuration (Config): Contains settings for body limits, timeouts, TLS options, routing behavior (e.g. case‑sensitivity, strict routing), and more. - Context Pool: A synchronized pool from which Contexts are acquired per request. This design minimizes allocations by recycling DefaultCtx (or CustomCtx) instances. - Router Stack: Organizes all registered routes. It is later processed into a tree structure for fast route‑matching. - MountFields: Support for mounting sub‑applications so that large APIs can be segmented into independent routers. - Hooks: Allow for custom behavior at critical points (e.g., on route registration, route naming, on listen, on shutdown, etc.). ## Request Processing Flow Fiber’s request processing is designed for performance and minimal overhead. When an HTTP request is received by the underlying fasthttp server, the flow is as follows: 1. Request Arrival: The fasthttp server receives the HTTP request. 2. Context Acquisition: The App calls AcquireCtx() to fetch a Context from the pool. 3. Context Reset: The acquired Context is reset (via DefaultCtx.Reset()) with the new request’s data. 4. Request Handling: The request handler (default or custom) is invoked. 5. Route Matching: The framework uses the next() (or nextCustom()) function to traverse the pre‑built route tree and find a matching route based on the URL and HTTP method. 6. Middleware Chain Execution: The matched route’s handler chain is executed in sequence. 7. Error Handling (if required): Any errors encountered trigger the registered error handler. 8. Response Generation: The response is sent back to the client. 9. Context Release: Finally, the Context is cleaned up and returned to the pool. ```mermaid flowchart LR R["HTTP Request (fasthttp)"] A["App.RequestHandler
(default or custom)"] C["Acquire Context
(from Pool)"] X["Reset Context
(DefaultCtx.Reset())"] N["Route Matching
(next() \/ nextCustom())"] M["Handler Chain Execution"] EH["Error Handling
(if needed)"] S["HTTP Response"] RC["Release Context
(to Pool)"] R --> A A --> C C --> X X --> N N --> M M --> EH EH --> S S --> RC ``` ### Additional Note Fiber minimizes memory allocations by reusing Context objects and uses an optimized route‑matching algorithm to rapidly determine the correct handler chain. ## Routing & Path Parsing Fiber allows you to register routes using helper methods (e.g. Get(), Post()) or by creating groups and sub‑routers. Internally, the route pattern is parsed by the parseRoute() function. This function decomposes the route string into segments: - Constant Segments: Fixed parts of the path (e.g. /api). - Parameter Segments: Dynamic parts that begin with a colon. For example, a route may be defined as: /api/\:userId<int> Here, the segment \:userId<int> is a parameter segment with a type constraint (an integer). - Constraints: Constraints (such as int, bool, datetime, or even regular expressions) are extracted from the parameter part and stored in the route’s metadata for validation at runtime. ```mermaid flowchart TD P["Route Pattern String
(e.g., '/api/\\:userId\\<int>')"] PA["parseRoute()"] RP[routeParser] RS["routeSegment(s)"] C["Constraints
(e.g., int, datetime, regex)"] PARAM[Extracted Parameter Names] P --> PA PA --> RP RP --> RS RS --> C RP --> PARAM ``` ### Explanation - parseRoute(): Takes a route string and returns a routeParser struct that includes a list of routeSegment objects. - routeSegment: Represents a portion of the route. If it is a parameter segment, it may include constraints that determine the allowed format (for example, ensuring that a parameter is an integer). - Extracted Parameter Names: These are later used to populate the request’s Context with the actual values parsed from the URL. ## Route Matching and Parameter Extraction When a request is processed, Fiber uses its pre‑computed route tree (the treeStack) to efficiently match the incoming URL against registered routes. 1. Normalization: The URL is normalized (converted to lowercase, trailing slashes trimmed) to create a “detection path.” 2. Tree Traversal: The route tree, grouped by common prefixes, is traversed based on the HTTP method. 3. Matching: Constant segments are compared exactly, while parameter segments extract dynamic values. 4. Constraint Validation: Extracted parameter values are validated against any defined constraints. ```mermaid flowchart TD A["Incoming Request URL
(e.g., '/api/john')"] B["Normalize URL
(lowercase, trim trailing slashes)"] C["Detection Path"] D["Traverse Route Tree
(treeStack based on method)"] E["Match Constant Segments"] F["Identify Parameter Segments
(e.g., ':userId')"] G["Extract Parameter Values"] H["Validate Constraints
(e.g., 'int', 'datetime', 'regex')"] I["Route Found"] A --> B B --> C C --> D D --> E E --> F F --> G G --> H H --> I ``` ### Insight This efficient matching mechanism leverages pre‑grouped routes to minimize comparisons, while dynamic segments allow for flexible URL structures and runtime validation. ## Middleware Chain Execution Once a matching route is found, Fiber executes the chain of middleware and route handlers sequentially. The process is as follows: 1. Initial Handler Execution: The first handler of the matched route is invoked. 2. Calling Next(): Each handler calls Ctx.Next() to pass control to the next handler in the chain. 3. Termination: When no further handlers remain, the chain terminates and the response is sent. ```mermaid flowchart TD A[Matched Route] B[Handler 1] C[Handler 2] D[Handler 3] E[Response Generation] A --> B B -- "Calls C via Next()" --> C C -- "Calls D via Next()" --> D D -- "No Next() available" --> E ``` ### Explanation - Each handler in the chain can perform operations (e.g. authentication, logging, transformation) before calling Next() to forward control. - This sequential processing ensures that middleware are executed in the order they were registered. - If an error occurs or a handler does not call Next(), the chain may be terminated early, and an error handler may be invoked. ### Observations Middleware are executed in the order they are registered. This sequential design allows each handler to perform tasks such as authentication, logging, or transformation before delegating to the next handler. ## Sub-Application Mounting & Grouping Fiber allows mounting sub‑applications (or sub‑routers) under specific path prefixes. This enables modular design of large APIs. The mounting process works as follows: 1. Defining a Mount Point: A parent application (or group) calls `Use` with a sub-app, which triggers the internal mount path logic. 2. Merging Mount Fields: The sub‑app’s mount fields are updated with the prefix of the parent, and its routes are integrated into the parent’s routing structure. 3. Processing Sub‑App Routes: During startup, the parent app collects routes from mounted sub‑apps and builds a unified route tree. ```mermaid flowchart TD A[Parent App] B["Sub-App (Mounted)"] C["Define Mount Point
(e.g. \'/admin\')"] D["Update MountFields
(assign mount path)"] E["Merge Sub-App Routes
(append to Router Stack)"] F[Generate Unified Route Tree] A --> C C --> B B --> D D --> E E --> F ``` ### Impact This mechanism enables large APIs to be broken down into smaller, maintainable modules while still benefiting from Fiber’s optimized routing and request handling. ## Route Tree Building Fiber builds a route tree (the treeStack) to optimize route matching. This involves grouping routes based on a prefix (usually the first few characters) to reduce the number of comparisons during a request. 1. Iterating Over the Router Stack: Each registered route is examined. 2. Computing the Tree Key: A key is computed from the route’s normalized path (e.g. the first 3 characters). 3. Grouping Routes: Routes are added to the appropriate branch of the tree. 4. Sorting: Within each group, routes are sorted based on their registration order (or position) to ensure the correct match is found. ```mermaid flowchart TD A["Router Stack
(All Registered Routes)"] B["Compute Tree Key
(e.g. first 3 characters)"] C["Group Routes by Key
(treeStack)"] D["Merge Global Routes
(key \'\' for global matches)"] E[Sort Routes within Groups] F[Optimized Route Tree] A --> B B --> C C --> D D --> E E --> F ``` ### Explanation - Building a route tree is an optimization step that reduces the matching overhead by limiting the search space to a subset of routes that share a common prefix. - The tree is rebuilt whenever new routes are registered, ensuring that the latest routing configuration is always used for matching. ## Context Lifecycle Management Fiber minimizes allocations by pooling Context objects. The lifecycle of a Context is as follows: 1. **Acquisition:** When a new HTTP request arrives, a Context is retrieved from the pool via `App.AcquireCtx()`. 2. **Reset:** The acquired Context is reset with the current `fasthttp.RequestCtx` to clear previous data and initialize new request‑specific values. 3. **Processing:** The Context is passed along the middleware and handler chain. 4. **Release:** After processing the request (or when an error occurs), the Context is released back to the pool via `App.ReleaseCtx()`, making it available for reuse. ```mermaid flowchart TD A["HTTP Request
(fasthttp)"] B["Acquire Context
(App.AcquireCtx())"] C["Reset Context
(DefaultCtx.Reset())"] D["Process Request
(Handlers & Middleware)"] E["Error Handling
(if needed)"] F["Release Context
(App.ReleaseCtx())"] A --> B B --> C C --> D D --> E E --> F ``` ### Key Benefit Reusing Context objects significantly reduces garbage collection overhead, ensuring Fiber remains fast and memory‑efficient even under heavy load. ## Preforking Mechanism To take full advantage of multi‑core systems, Fiber offers a prefork mode. In this mode, the master process spawns several child processes that listen on the same port. On Linux, this is typically done with `SO_REUSEPORT`; on Windows, Fiber falls back to a `SO_REUSEADDR`-based behavior that does not provide identical kernel-level load-distribution semantics. ```mermaid flowchart LR M["Master Process (App)"] C[Child Processes] GOMAX["Set GOMAXPROCS(1)"] REQ[Handle HTTP Requests] WM["watchMaster()"] M -->|Spawns| C C --> GOMAX C -->|Processes| REQ C --> WM ``` ### Explanation - Master Process: The main process determines the number of available CPU cores and spawns that many child processes. - Child Processes: Each child sets GOMAXPROCS(1) to run on a single CPU core and listens on the shared port. - watchMaster(): Each child process runs a watchdog routine to monitor the master process; if the master exits (or its parent process ID becomes 1 on Unix‑like systems), the child terminates gracefully. ### Detailed Preforking Workflow Fiber’s prefork mode uses OS‑level mechanisms to allow multiple processes to listen on the same port. Here’s a more detailed look: 1. Master Process Spawning: The master process detects the number of CPU cores and spawns that many child processes. 2. Child Process Initialization: Each child process sets GOMAXPROCS(1) so that it runs on a single core. 3. Binding to Port: - Linux: child processes use `SO_REUSEPORT` (for example through reuseport helpers) so multiple workers can bind the same address/port with kernel-level distribution. - Windows: Fiber uses a `SO_REUSEADDR` fallback path; behavior is platform-dependent and should not be treated as equivalent to Linux `SO_REUSEPORT`. 4. Parent Monitoring: Each child runs a watchdog function (watchMaster()) to monitor the master process; if the master terminates, children exit. 5. Request Handling: Each child independently handles incoming HTTP requests. ```mermaid flowchart TD A[Master Process] B[Determine CPU Cores] C[Spawn Child Processes] D["Child Process Initialization
(GOMAXPROCS(1))"] E["Bind to Port
(reuseport)"] F["Run watchMaster()
(Monitor Parent)"] G[Handle HTTP Requests] A --> B B --> C C --> D D --> E E --> F F --> G ``` #### Explanation - Preforking improves performance by allowing multiple processes to handle requests concurrently. - Linux `SO_REUSEPORT` and Windows fallback behavior are functionally different; do not assume identical security or scheduling characteristics across operating systems. - The watchdog routine in each child ensures that they exit if the master process is no longer running, maintaining process integrity. ### Security Considerations Prefork intentionally relaxes strict single-owner assumptions on a listening socket. In multi-tenant or shared-host environments, a local co-resident attacker with sufficient local privileges may attempt to bind to the same address/port and compete for or observe traffic, depending on OS behavior, account boundaries, and deployment configuration. - **Privilege and user-boundary assumptions:** Prefork is safest when all participating workers run under the same dedicated service identity and within an isolated trust boundary. - **Linux vs Windows behavior:** Linux `SO_REUSEPORT` provides explicit multi-listener semantics; Windows fallback behavior differs and should be evaluated carefully before enabling prefork in sensitive environments. - **Strict ownership requirement:** If you require strict single-owner port semantics, run Fiber **without** prefork. - **Hardening guidance:** Prefer a dedicated service user, strong container/VM isolation, and avoid shared host namespaces between unrelated workloads. ## Redirection & Flash Messages Fiber’s redirection mechanism is implemented via the Redirect struct. This structure allows not only setting a new location for redirection but also passing along flash messages and old input data via a special cookie. ```mermaid flowchart LR R[Redirect Struct] RP[redirectPool] FM["Flash Messages \/ Old Inputs"] M["Methods:
To(), Route(), Back()"] LH[Set Location Header] CK["Flash Cookie
(fiber\_flash)"] R -->|Acquired from| RP R --> FM R --> M M --> LH FM -->|Serialized| CK ``` ### Explanation - Redirect Struct: Retrieved from a pool (to minimize allocations), it stores redirection settings such as the HTTP status code (defaulting to 303 See Other) and any flash messages. - Flash Messages & Old Inputs: These are collected via methods like With() or WithInput() and then serialized and stored in a cookie named fiber_flash. - Redirection Methods: The To(), Route(), and Back() methods determine the target URL and set the Location header accordingly. ### Flash Message Handling in Redirection When performing redirections, Fiber can send flash messages or preserve old input data. This process involves: 1. Collecting Flash Data: When a redirect is initiated, developers can add flash messages via Redirect.With() or old input data via Redirect.WithInput(). 2. Serialization: The flash messages and input data are serialized (using a fast marshalling method) into a byte sequence. 3. Setting a Cookie: The serialized data is stored in a special cookie (named fiber_flash) that will be sent to the client. 4. Retrieval & Clearing: On the subsequent request, the flash data is read from the cookie, deserialized, and then cleared. ```mermaid flowchart TD A[Initiate Redirect] B["Add Flash Messages
(With(), WithInput())"] C[Serialize Flash Data] D["Set Flash Cookie
(\'fiber\_flash\')"] E[Client Receives Redirect] F[Next Request Reads Flash Cookie] G["Deserialize & Clear Flash Data"] A --> B B --> C C --> D D --> E E --> F F --> G ``` #### Explanation - Flash messages provide a way to pass transient data (such as notifications or error messages) to the next request after a redirect. - The data is stored temporarily in a cookie, which is then read and cleared upon processing the next request. - This mechanism is essential for implementing post‑redirect‑get patterns and ensuring a smooth user experience. ## Hooks, Error Handling & Context Lifecycle ### Hooks Fiber provides a comprehensive hook system that allows you to run custom functions at key moments: - OnRoute: Called when a route is registered. - OnName: Invoked when a route is assigned a name. - OnGroup: Triggered when a group is created. - OnListen: Runs when the server starts listening. - OnPreShutdown: Runs before the application shuts down. - OnPostShutdown: Runs after shutdown and receives the shutdown result. - OnFork: Invoked when a child process is forked. - OnMount: Used when a sub‑application is mounted. ```mermaid flowchart TD H[Hooks] OR[OnRoute] ON[OnName] OG[OnGroup] OL[OnListen] OPRS[OnPreShutdown] OPOS[OnPostShutdown] OF[OnFork] OM[OnMount] H --> OR H --> ON H --> OG H --> OL H --> OPRS H --> OPOS H --> OF H --> OM ``` #### Explanation - Hooks provide extension points for developers and maintainers to inject custom logic without modifying the core Fiber code. - They are executed at various stages (for example, every time a new route is registered, the OnRoute hooks are executed to allow for logging, validation, or transformation of the route). ### Error Handling & Context Lifecycle Fiber’s DefaultCtx (or CustomCtx) represents the per‑request context. The lifecycle is as follows: - Acquire: A Context is obtained from the pool at the beginning of a request. - Processing: The context is passed along to the route handlers and middleware. - Error Handling: If an error occurs (e.g., route not found, method not allowed, or a panic in the handler), Fiber calls the registered error handler. Errors such as ErrMethodNotAllowed or StatusNotFound are generated as needed. - Release: Once the request is processed, the Context is released back into the pool for reuse. ```mermaid flowchart LR AC["Acquire Context
(from Pool)"] HP["Handle Request
(Handlers & Middleware)"] EH["Error Handling
(if needed)"] RC["Release Context
(to Pool)"] AC --> HP HP --> EH EH --> RC ``` #### Explanation - This lifecycle ensures that Fiber minimizes allocations by reusing Context objects. - Errors are propagated and handled consistently, and the context is properly reset after every request. --- ## 📚 Learning Resources ## Interactive Tools in These Docs The documentation itself ships hands-on tools, no setup required: - [Route Matcher](./route-matcher.md): fire requests at your own route table and see which route wins, why the others lose, and which parameters are extracted - [Build Your First App, Step by Step](../intro.md#build-your-first-app-step-by-step): grow one `main.go` from Hello World to a small JSON API with simulated requests - [Anatomy of a route](../guide/routing.md#anatomy-of-a-route) and the [middleware chain visualizer](../guide/routing.md#middleware): interactive breakdowns inside the routing guide ## Interactive Learning Platforms Looking to practice Fiber concepts through hands-on exercises? Here are some community-driven learning resources: ### Go Interview Practice - Fiber Challenges A comprehensive platform offering progressive Fiber challenges that complement the official documentation. ![Learning Path Overview](/img/learning-resources/fiber-learning-path.png) **What You'll Learn:** - **High-Performance APIs** - Build ultra-fast RESTful APIs with zero-allocation routing - **Middleware & Security** - Implement custom middleware, rate limiting, CORS, and authentication - **Request Validation** - Input validation, error handling, and data transformation - **Authentication & JWT** - Secure authentication systems with JWT tokens and API key validation ![Challenge Interface](/img/learning-resources/fiber-challenge-interface.png) **Challenge Roadmap:** 1. **Basic Routing** - Setup Fiber, routes, and handlers (Beginner) 2. **Middleware & CORS** - Custom middleware and rate limiting (Intermediate) 3. **Validation & Errors** - Input validation and error handling (Intermediate) 4. **Authentication** - JWT tokens and API key validation (Advanced) ![Fiber Framework Overview](/img/learning-resources/fiber-framework-overview.png) ![Interactive Learning Experience](/img/learning-resources/fiber-learning-experience.png) [Explore Fiber Challenges →](https://rezasi.github.io/go-interview-practice/fiber) | [GitHub Repository →](https://github.com/RezaSi/go-interview-practice) --- ## 🎯 Route Matcher Edit the route table or the request and watch which route wins, why the others lose, and which parameters are extracted. Routes are tried in registration order, exactly like in a real Fiber app: :::note The playground simulates Fiber's matcher with default settings (case-insensitive, non-strict routing). `datetime` and custom constraints are not simulated, and `regex()` runs on the JS engine instead of Go's RE2. ::: The [routing guide](../guide/routing.md) explains the full syntax: [parameters](../guide/routing.md#parameters), wildcards, literal separators, and [constraints](../guide/routing.md#constraints). --- ## 🐛 Advanced Format ## MsgPack Fiber lets you use MessagePack for efficient binary serialization. Use one of the popular Go libraries below to encode and decode data in handlers. - Fiber can bind requests with the `application/vnd.msgpack` content type out of the box. See the [Binding documentation](../api/bind.md#msgpack) for details. - Use `Bind().MsgPack()` to bind data to structs, similar to JSON. `Ctx.AutoFormat()` responds with MsgPack when the `Accept` header is `application/vnd.msgpack`. See the [AutoFormat documentation](../api/ctx.md#autoformat) for more. ### Recommended Libraries - [github.com/vmihailenco/msgpack](https://pkg.go.dev/github.com/vmihailenco/msgpack) — A widely used, feature-rich MsgPack library. - [github.com/shamaton/msgpack/v3](https://pkg.go.dev/github.com/shamaton/msgpack/v3) — High-performance MsgPack library. ### Installation Install either library using: ```bash go get github.com/vmihailenco/msgpack # or go get github.com/shamaton/msgpack/v3 ``` > **Note:** Fiber doesn't bundle a MsgPack implementation because it's outside the Go standard library. Pick one of the popular libraries in the ecosystem; the two below are widely used and well maintained. ### Example: Using `shamaton/msgpack/v3` ```go import ( "github.com/gofiber/fiber/v3" "github.com/shamaton/msgpack/v3" ) type User struct { Name string `msgpack:"name"` // tag may vary depending on your MsgPack library Age int `msgpack:"age"` } func main() { app := fiber.New(fiber.Config{ // Optional: Set custom MsgPack encoder/decoder MsgPackEncoder: msgpack.Marshal, MsgPackDecoder: msgpack.Unmarshal, }) app.Post("/msgpack", func(c fiber.Ctx) error { var user User if err := c.Bind().MsgPack(&user); err != nil { return err } // Content type will be set automatically to application/vnd.msgpack return c.MsgPack(user) }) app.Listen(":3000") } ``` ## CBOR Fiber doesn't ship with a CBOR implementation. Use a library such as [fxamacker/cbor](https://github.com/fxamacker/cbor) to add encoding and decoding. - Use `Bind().CBOR()` to bind CBOR to structs. `Ctx.AutoFormat()` replies with CBOR when the `Accept` header is `application/cbor`. See the [AutoFormat documentation](../api/ctx.md#autoformat) for details. ```bash go get github.com/fxamacker/cbor/v2 ``` Configure Fiber with the chosen library: ```go import ( "github.com/gofiber/fiber/v3" "github.com/fxamacker/cbor/v2" ) func main() { app := fiber.New(fiber.Config{ CBOREncoder: cbor.Marshal, CBORDecoder: cbor.Unmarshal, }) type User struct { Name string `cbor:"name"` Age int `cbor:"age"` } app.Post("/cbor", func(c fiber.Ctx) error { var user User if err := c.Bind().CBOR(&user); err != nil { return err } // Content type will be set automatically to application/cbor return c.CBOR(user) }) app.Listen(":3000") } ``` --- ## 🧠 Go Context ## Fiber Context as `context.Context` Fiber's [`Ctx`](../api/ctx.md) implements Go's [`context.Context`](https://pkg.go.dev/context#Context) interface. You can pass `c` directly to functions that expect a `context.Context` without adapters, but only to read values from it. `Ctx` is a context that can never be canceled: `Deadline`, `Done`, and `Err` always report no deadline, `nil`, and `nil`, whatever you set with `c.SetContext`. For cancellation and deadlines, use `c.Context()`. :::caution The `fiber.Ctx` instance is only valid within the lifetime of the handler. It is reused for subsequent requests, so avoid storing `c` or using it in goroutines that outlive the handler. For asynchronous work, call `c.Context()` inside the handler to obtain a `context.Context` that can safely be used after the handler returns. By default, this returns `context.Background()` unless a custom context was provided with `c.SetContext`. ::: ```go title="Example" func doSomething(ctx context.Context) { // ... your logic here } app.Get("/", func(c fiber.Ctx) error { doSomething(c) // c satisfies context.Context return nil }) ``` ### Using context outside the handler `fiber.Ctx` is recycled after each request. If you need a context that lives longer—for example, for work performed in a new goroutine—obtain it with `c.Context()` before returning from the handler. ```go title="Async work" app.Get("/job", func(c fiber.Ctx) error { ctx := c.Context() go performAsync(ctx) return c.SendStatus(fiber.StatusAccepted) }) ``` You can customize the base context by calling `c.SetContext` before requesting it. This changes what `c.Context()` returns for the rest of the request; it does not make `c` itself cancelable. ```go app.Get("/job", func(c fiber.Ctx) error { c.SetContext(context.WithValue(context.Background(), "requestID", "123")) ctx := c.Context() go performAsync(ctx) return nil }) ``` ### Retrieving Values `Ctx.Value` is backed by [Locals](../api/ctx.md#locals). Values stored with `c.Locals` are accessible through `Value` or standard `context.WithValue` helpers. ```go title="Locals and Value" app.Get("/", func(c fiber.Ctx) error { c.Locals("role", "admin") role := c.Value("role") // returns "admin" return c.SendString(role.(string)) }) ``` ## Working with `RequestCtx` and `fasthttpctx` The underlying [`fasthttp.RequestCtx`](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) can be accessed via `c.RequestCtx()`. This exposes low-level APIs and the extra context support provided by `fasthttpctx`. ```go title="Accessing RequestCtx" app.Get("/raw", func(c fiber.Ctx) error { fctx := c.RequestCtx() // use fasthttp APIs directly fctx.Response.Header.Set("X-Engine", "fasthttp") return nil }) ``` `fasthttpctx` enables `fasthttp` to satisfy the `context.Context` interface. `Deadline` always reports no deadline. `Done` closes only when the server shuts down, and `Err` then returns `context.Canceled`. Note that this is the `fasthttp.RequestCtx`, not Fiber's `Ctx`, whose `Done` is always `nil`. :::caution `Done` does not fire when an individual client disconnects. To stop a long-running handler at a deadline, wrap `c.Context()` with `context.WithTimeout` as shown below. ::: ## Context Helpers Fiber and its middleware expose a number of helper functions that retrieve request-scoped values from the context. ### Request ID The RequestID middleware stores the generated identifier in the context. Use `requestid.FromContext` to read it later. ```go app.Use(requestid.New()) app.Get("/", func(c fiber.Ctx) error { id := requestid.FromContext(c) return c.SendString(id) }) ``` ### CSRF The CSRF middleware provides helpers to fetch the token or the handler attached to the current context. ```go app.Use(csrf.New()) app.Get("/form", func(c fiber.Ctx) error { token := csrf.TokenFromContext(c) return c.SendString(token) }) ``` ```go title="Deleting a token" app.Post("/logout", func(c fiber.Ctx) error { handler := csrf.HandlerFromContext(c) if handler != nil { // Invalidate the token on logout _ = handler.DeleteToken(c) } // ... other logout logic return c.SendString("Logged out") }) ``` ### Session Sessions are stored on the context and can be retrieved via `session.FromContext`. ```go app.Use(session.New()) app.Get("/", func(c fiber.Ctx) error { sess := session.FromContext(c) count := sess.Get("visits") return c.JSON(fiber.Map{"visits": count}) }) ``` ### Basic Authentication After successful authentication, the username is available with `basicauth.UsernameFromContext`. Passwords in `Users` must be pre-hashed. ```go app.Use(basicauth.New(basicauth.Config{ Users: map[string]string{ // "secret" hashed using SHA-256 "admin": "{SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=", }, })) app.Get("/", func(c fiber.Ctx) error { user := basicauth.UsernameFromContext(c) return c.SendString(user) }) ``` ### Key Authentication For API key authentication, the extracted token is stored in the context and accessible via `keyauth.TokenFromContext`. ```go app.Use(keyauth.New()) app.Get("/", func(c fiber.Ctx) error { token := keyauth.TokenFromContext(c) return c.SendString(token) }) ``` ## Using `context.WithValue` and Friends Standard helpers such as `context.WithValue`, `context.WithTimeout`, or `context.WithCancel` can wrap the request context when needed. Derive them from `c.Context()`, not from `c`: `c` can never be canceled, so a context derived from it would never inherit a deadline or a cancellation. ```go app.Get("/job", func(c fiber.Ctx) error { ctx, cancel := context.WithTimeout(c.Context(), 5*time.Second) defer cancel() // pass ctx to async operations that honor cancellation if err := doWork(ctx); err != nil { return err } return c.SendStatus(fiber.StatusOK) }) ``` ### Context Cancellation with Goroutines in Fiber When starting asynchronous work inside a handler, Fiber does not cancel the base `fiber.Ctx` automatically. By wrapping the request context with `context.WithTimeout`, you can create a derived context that honors deadlines and cancellation signals. The goroutine checks `ctx.Done()` before sending a result. If the deadline elapses, the goroutine exits early and avoids leaking resources. The handler then waits for either: - a result from the goroutine, or - the `context timeout` (which returns a 504 Gateway Timeout) This pattern ensures that long-running operations (database queries, external API calls, background tasks) do not continue running after the request has ended. ```go func Handler(c fiber.Ctx) error { ctx, cancel := context.WithTimeout(c.Context(), 2*time.Second) defer cancel() resultChan := make(chan string, 1) go func() { select { case <-time.After(3 * time.Second): select { case <-ctx.Done(): return case resultChan <- "done": } case <-ctx.Done(): return } }() select { case res := <-resultChan: return c.SendString(res) case <-ctx.Done(): return c.Status(fiber.StatusGatewayTimeout).SendString("timeout") } } ``` This approach provides safe cancellation semantics for goroutine-based work while allowing you to integrate Fiber handlers with context-aware APIs. ## Summary - `fiber.Ctx` satisfies `context.Context` but its `Deadline`, `Done`, and `Err` methods are currently no-ops. - `RequestCtx` exposes the raw `fasthttp` context. Its `Done` channel closes only on server shutdown, not on client disconnect. - Use `fiber.StoreInContext(c, key, value)` to store request-scoped values in both `c.Locals()` and `c.Context()` when values must be available through either API. - Middleware helpers like `requestid.FromContext` or `session.FromContext` make it easy to retrieve request-scoped data. - Standard helpers such as `context.WithTimeout` can wrap `fiber.Ctx` to create fully featured derived contexts inside handlers. - `fiber.Config.PassLocalsToContext` controls whether Fiber context helpers also propagate values into the request `context.Context` for Fiber-backed contexts when using `StoreInContext`. It defaults to `false` for backward compatibility, while `ValueFromContext` keeps reading from `c.Locals()`. - Use `c.Context()` to obtain a `context.Context` that can outlive the handler, and `c.SetContext()` to customize it with additional values or deadlines. With these tools, you can seamlessly integrate Fiber applications with Go's context-based APIs and manage request-scoped data effectively. --- ## 🐛 Error Handling ## Catching Errors Return errors from route handlers and middleware so Fiber can handle them centrally. ```go app.Get("/", func(c fiber.Ctx) error { // Pass error to Fiber return c.SendFile("file-does-not-exist") }) ``` Fiber does not recover from [panics](https://go.dev/blog/defer-panic-and-recover) by default. Add the `Recover` middleware to catch panics in any handler: ```go title="Example" 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`). ```go title="Example" 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](https://godoc.org/github.com/gofiber/fiber#Error), the response uses the embedded status code and message. ```go title="Example" // 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`](../api/fiber.md#errorhandler) 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. ```go title="Example" // 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](https://echo.labstack.com/) and [Express](https://expressjs.com/) frameworks for inspiring parts of this error-handling approach. --- ## 🔬 Extractors The extractors package provides shared value extraction utilities for Fiber middleware packages. It helps reduce code duplication across middleware packages while ensuring consistent behavior and security practices. ## Overview The `github.com/gofiber/fiber/v3/extractors` module provides standardized value extraction utilities integrated into Fiber's middleware ecosystem. This approach: - **Reduces Code Duplication**: Eliminates redundant extractor implementations across middleware packages - **Ensures Consistency**: Maintains identical behavior and security practices across all extractors - **Simplifies Maintenance**: Changes to extraction logic only need to be made in one place - **Enables Direct Usage**: Middleware can import and use extractors directly - **Improves Performance**: Shared, optimized extraction functions reduce overhead ## What Are Extractors? Extractors are utilities that middleware uses to get values from different parts of HTTP requests: ### Available Extractors - `FromAuthHeader(authScheme string)`: Extract from Authorization header with optional scheme - `FromCookie(key string)`: Extract from HTTP cookies - `FromParam(param string)`: Extract from URL path parameters - `FromForm(param string)`: Extract from form data - `FromHeader(header string)`: Extract from custom HTTP headers - `FromQuery(param string)`: Extract from URL query parameters - `FromCustom(key string, fn func(fiber.Ctx) (string, error))`: Define custom extraction logic with metadata - `Chain(extractors ...Extractor)`: Chain multiple extractors with fallback logic - `Extractor.Contains(pred func(Extractor) bool)`: Check whether this extractor, or any nested chained extractor, matches a predicate ### Extractor Structure Each `Extractor` contains: ```go type Extractor struct { Extract func(fiber.Ctx) (string, error) // Extraction function Key string // Parameter/header name Source Source // Source type for inspection AuthScheme string // Auth scheme (FromAuthHeader) Chain []Extractor // Chained extractors } ``` - **Headers**: `Authorization`, `X-API-Key`, custom headers - **Cookies**: Session cookies, authentication tokens - **Query Parameters**: URL parameters like `?token=abc123` - **Form Data**: POST body form fields - **URL Parameters**: Route parameters like `/users/:id` ### Chain Behavior The `Chain` function creates extractors that try multiple sources in order: - Returns the first successful extraction (non-empty value with no error) - If all extractors fail, returns the last error encountered or `ErrNotFound` - **Robust error handling**: Skips extractors with `nil` Extract functions - **Cycle prevention**: Detects recursive chain re-entry and returns `ErrChainCycle` - Preserves the source and key from the first extractor for metadata - Stores a defensive copy of all chained extractors for introspection via the `Chain` field ### Chain Introspection Use `Contains` to inspect an extractor tree with a predicate: ```go chain := extractors.Chain( extractors.FromHeader("X-CSRF-Token"), extractors.FromCookie("CSRF"), ) hasCSRFCookie := chain.Contains(func(e extractors.Extractor) bool { return e.Source == extractors.SourceCookie && e.Key == "CSRF" }) ``` ## Why Middleware Uses Extractors Middleware needs to extract values from requests for authentication, authorization, and other purposes. Extractors provide: - **Security Awareness**: Different sources have different security implications - **Fallback Support**: Try multiple sources if the first one doesn't have the value - **Consistency**: Same extraction logic across all middleware packages - **Source Tracking**: Know where values came from for security decisions ## Usage Examples ### Basic Usage ```go // KeyAuth middleware extracts key from header app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromHeader("Middleware-Key"), })) ``` ### Fallback Chains ```go // Try multiple sources in order tokenExtractor := extractors.Chain( extractors.FromHeader("Middleware-Key"), // Try header first extractors.FromCookie("middleware_key"), // Then cookie extractors.FromQuery("middleware_key"), // Finally query param ) app.Use(keyauth.New(keyauth.Config{ Extractor: tokenExtractor, })) ``` ## Configuring Middleware That Uses Extractors ### Authentication Middleware ```go // KeyAuth middleware (default: FromAuthHeader) app.Use(keyauth.New(keyauth.Config{ // Default extracts from Authorization header // Extractor: extractors.FromAuthHeader("Bearer"), })) // Custom header extraction app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromHeader("X-API-Key"), })) // Multiple sources with secure fallback app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.Chain( extractors.FromAuthHeader("Bearer"), // Secure first extractors.FromHeader("X-API-Key"), // Then custom header extractors.FromQuery("api_key"), // Least secure last ), })) ``` ### Session Middleware ```go // Session middleware (default: FromCookie) app.Use(session.New(session.Config{ // Default extracts from session_id cookie // Extractor: extractors.FromCookie("session_id"), })) // Custom cookie name app.Use(session.New(session.Config{ Extractor: extractors.FromCookie("my_session"), })) ``` ### CSRF Middleware ```go // CSRF middleware (default: FromHeader) app.Use(csrf.New(csrf.Config{ // Default extracts from X-CSRF-Token header // Extractor: extractors.FromHeader("X-CSRF-Token"), })) // Form-based CSRF (less secure, use only if needed) app.Use(csrf.New(csrf.Config{ Extractor: extractors.Chain( extractors.FromHeader("X-CSRF-Token"), // Secure first extractors.FromForm("_csrf"), // Form fallback ), })) ``` ## Security Considerations ### Source Characteristics Different extraction sources have different security properties and use cases: #### Headers (Generally Preferred) - **Authorization Header**: Standard for authentication tokens, widely supported - **Custom Headers**: Application-specific, less likely to be logged by default - **Considerations**: Can be intercepted without HTTPS, may be stripped by proxies #### Cookies (Good for Sessions) - **Session Cookies**: Designed for secure client-side storage - **Considerations**: Require proper `Secure`, `HttpOnly`, and `SameSite` flags - **Best for**: Session management, remember-me tokens #### Query Parameters (Use Sparingly) - **Query parameters**: Convenient for simple APIs and debugging - **Considerations**: Always visible in URLs, logged by servers/proxies, stored in browser history - **Best for**: Non-sensitive parameters, public identifiers #### Form Data (Context Dependent) - **POST Bodies**: Suitable for form submissions and API requests - **Considerations**: Avoid putting sensitive data in query strings; ensure request bodies aren’t logged and use the correct content type - **Best for**: User-generated content, file uploads ### Security Best Practices 1. **Use HTTPS**: Encrypt all traffic to protect extracted values in transit 2. **Validate Input**: Always validate and sanitize extracted values 3. **Log Carefully**: Avoid logging sensitive values from any source 4. **Choose Appropriate Sources**: Match the source to your security requirements 5. **Test Thoroughly**: Verify extraction works in your environment 6. **Monitor Security**: Watch for extraction failures or unusual patterns ### Chain Ordering Strategy When using multiple sources, order them by your security preferences: ```go // Example: Prefer headers, fall back to cookies, then query extractors.Chain( extractors.FromAuthHeader("Bearer"), // Standard auth extractors.FromCookie("auth_token"), // Secure storage extractors.FromQuery("token"), // Public fallback ) ``` The "best" source depends on your specific use case, security requirements, and application architecture. ### Common Security Issues #### Leaky URLs ```go // ❌ DON'T: API keys in URLs (visible in logs, history, bookmarks) app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromQuery("api_key"), // PROBLEMATIC })) // ✅ DO: API keys in headers (not visible in URLs) app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromHeader("X-API-Key"), // BETTER })) ``` #### Session Tokens in Query Parameters ```go // ❌ DON'T: Session tokens in URLs (can be bookmarked, leaked) app.Use(session.New(session.Config{ Extractor: extractors.FromQuery("session"), // PROBLEMATIC })) // ✅ DO: Session tokens in cookies (designed for this purpose) app.Use(session.New(session.Config{ Extractor: extractors.FromCookie("session_id"), // BETTER })) ``` #### Form-Only CSRF Tokens While the default extractor uses headers, some implementations use form fields, which is fine if you don't have AJAX or API clients: ```go // ❌ DON'T: CSRF tokens only in forms (breaks AJAX, API calls) app.Use(csrf.New(csrf.Config{ Extractor: extractors.FromForm("_csrf"), // LIMITED })) // ✅ DO: Header-first with form fallback (works everywhere) app.Use(csrf.New(csrf.Config{ Extractor: extractors.Chain( extractors.FromHeader("X-CSRF-Token"), // PREFERRED extractors.FromForm("_csrf"), // FALLBACK ), })) ``` ### Understanding Trade-offs **No extractor is universally "secure" - security depends on:** - Whether you're using HTTPS - How you configure cookies (Secure, HttpOnly, SameSite flags) - Your logging and monitoring setup - The sensitivity of the data being extracted - Your threat model and security requirements Choose extractors based on your specific use case and security needs, not blanket "secure" vs "insecure" labels. ## Standards Compliance ### Authorization Header (RFC 9110 & RFC 7235) The `FromAuthHeader` extractor provides comprehensive RFC compliance with strict security validation: #### RFC 9110 Compliance (Authorization Header Format) - **Section 11.6.2 Format**: Enforces `credentials = auth-scheme 1*SP token68` structure - **1*SP Requirement**: Validates exactly one or more spaces between auth-scheme and token - **Case-insensitive scheme matching**: `Bearer`, `bearer`, `BEARER` all work correctly - **Proper whitespace handling**: Rejects tabs between scheme and token (only spaces allowed) #### RFC 7235 Token68 Validation The extractor implements strict token68 character validation per RFC 7235: - **Allowed characters**: `A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, `~`, `+`, `/`, `=` - **Padding rules**: `=` characters only allowed at the end of tokens - **Security validation**: Prevents tokens starting with `=` or having non-padding characters after `=` - **Whitespace rejection**: Rejects tokens containing spaces, tabs, or any other whitespace #### Security Features - **Header injection prevention**: Strict parsing prevents malformed authorization headers from bypassing authentication - **Token validation**: Ensures extracted tokens conform to standards, preventing authentication bypass - **Consistent error handling**: Returns `ErrNotFound` for all invalid cases #### Examples ```go // Standard usage - strict validation extractor := extractors.FromAuthHeader("Bearer") // ✅ Valid cases: // "Bearer abc123" -> "abc123" // "bearer ABC123" -> "ABC123" (case-insensitive scheme) // "Bearer token123=" -> "token123=" (valid padding) // "Bearer token==" -> "token==" (valid multiple padding) // ❌ Invalid cases (all return ErrNotFound): // "Bearer abc def" -> rejected (space in token) // "Bearer abc\tdef" -> rejected (tab in token) // "Bearer =abc" -> rejected (padding at start) // "Bearer ab=cd" -> rejected (padding in middle) // "Bearer token" -> rejected (multiple spaces after scheme) // "Bearer\ttoken" -> rejected (tab after scheme) // "Bearertoken" -> rejected (no space after scheme) // Raw header extraction (no validation) rawExtractor := extractors.FromAuthHeader("") // "CustomAuth anything goes here" -> "CustomAuth anything goes here" ``` #### Benefits - **Standards Compliance**: Full adherence to HTTP authentication RFCs - **Security Hardening**: Prevents common authentication bypass vulnerabilities - **Consistent Behavior**: Reliable parsing across different client implementations - **Developer Confidence**: Clear validation rules reduce authentication bugs ## Troubleshooting ### Extraction Fails **Problem**: Middleware returns "value not found" or authentication fails **Solutions**: 1. Check if the expected header/cookie/query parameter is present 2. Verify the key name matches exactly (headers are case-insensitive; params/cookies/query keys are case-sensitive) 3. Ensure the request uses the correct HTTP method (GET vs POST) 4. Check if middleware is configured with the right extractor **Debug Example**: ```go // Add simple debug logging (avoid logging secrets in production) app.Use(func(c fiber.Ctx) error { hdr := c.Get("X-API-Key") cookie := c.Cookies("session_id") if hdr != "" || cookie != "" { log.Printf("debug: X-API-Key present=%t, session_id present=%t", hdr != "", cookie != "") } return c.Next() }) ``` ### Wrong Source Used **Problem**: Values extracted from unexpected sources **Solutions**: 1. Check middleware configuration order 2. Verify chain order (first successful extraction wins) 3. Use more specific extractors when needed ### Security Warnings **Problem**: Getting security warnings in logs **Solutions**: 1. Switch to more secure sources (headers/cookies) 2. Use HTTPS to encrypt traffic 3. Review if sensitive data should be in that source ## Advanced Usage ### Custom Extraction Logic Extractors support custom extractors for complex scenarios: ```go // Extract from custom logic (rarely needed) customExtractor := extractors.FromCustom("my-source", func(c fiber.Ctx) (string, error) { // Complex extraction logic if value := c.Locals("computed_token"); value != nil { return value.(string), nil } return "", extractors.ErrNotFound }) ``` :::warning **Custom extractors break source awareness.** When you use `FromCustom`, middleware cannot determine where the value came from, which means: - **No automatic security warnings** for potentially insecure sources - **No source-based logging** or monitoring capabilities - **Developer responsibility** for ensuring the extraction is secure and appropriate **Only use `FromCustom` when:** - Standard extractors don't meet your needs - You've carefully evaluated the security implications - You're confident in the security of your custom extraction logic - You understand that middleware cannot provide source-aware security guidance **Note:** If you pass `nil` as the function parameter, `FromCustom` will return an extractor that always fails with `ErrNotFound`. ::: ### Multiple Middleware Coordination When using multiple middleware that extract values, ensure they don't conflict: ```go // Good: Different sources for different purposes app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromHeader("X-API-Key"), })) app.Use(session.New(session.Config{ Extractor: extractors.FromCookie("session_id"), })) // Avoid: Same source for different middleware app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromCookie("token"), // API auth })) app.Use(session.New(session.Config{ Extractor: extractors.FromCookie("token"), // Session - CONFLICT! })) ``` --- ## ⚡ Make Fiber Faster ## Custom JSON Encoder/Decoder Fiber defaults to the standard `encoding/json` for stability and reliability. If you need more speed, consider these libraries: - [goccy/go-json](https://github.com/goccy/go-json) - [bytedance/sonic](https://github.com/bytedance/sonic) - [segmentio/encoding](https://github.com/segmentio/encoding) - [minio/simdjson-go](https://github.com/minio/simdjson-go) ```go title="Example" 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](../client/rest.md#setjsonmarshal) - [Set custom JSON decoder for client](../client/rest.md#setjsonunmarshal) - [Set custom JSON encoder for application](../api/fiber.md#jsonencoder) - [Set custom JSON decoder for application](../api/fiber.md#jsondecoder) ## 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 through [`Config.RegexHandler`](../api/fiber.md#regexhandler). This lets you try high-performance engines such as [coregex](https://github.com/coregx/coregex) on the matching path. ### Configure `RegexHandler` Set `RegexHandler` to the compile function you want Fiber to use for `regex()` constraints. ```go title="Example" 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", func(c fiber.Ctx) error { return c.SendString("ID: " + c.Params("id")) }) } ``` You can also set it explicitly to the standard library default: ```go title="Example" package main import ( "regexp" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New(fiber.Config{ RegexHandler: regexp.MustCompile, }) _ = app } ``` ### Notes - `RegexHandler` only affects `regex()` parameter constraints - invalid patterns still panic during route registration because Fiber uses `MustCompile`-style semantics - Fiber may invoke `RegexHandler` more 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](../intro.md#zero-allocation): why `Ctx` values are reused across requests and how to copy them safely - [Benchmarks](../extra/benchmarks.md): how Fiber compares to other frameworks --- ## 🎭 Grouping :::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. ```go 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. ```go 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. ```go 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`](../api/app.md#route) groups routes under a common prefix declared inside a single callback, with an optional name prefix. It is shorthand for nesting with `Group`. ```go 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`](../api/app.md#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. ```go 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`](../api/app.md#use)), not the exact-path `App.All`, so it also runs for sub-paths of the chain path. ::: --- ## 🔄 Reverse Proxy ## Proxies Running Fiber behind a reverse proxy is a common production setup. Reverse proxies can handle: - **HTTPS/TLS termination** (offloading SSL certificates) - **Protocol upgrades** (HTTP/2, HTTP/3 support) - **Request routing & load balancing** - **Caching & compression** - **Security features** (rate limiting, WAF, DDoS mitigation) Some Fiber features (like [`SendEarlyHints`](../api/ctx.md#sendearlyhints)) require **HTTP/2 or newer**, which is easiest to enable using a reverse proxy. ### Popular Reverse Proxies - [Nginx](https://nginx.org/) - [Traefik](https://traefik.io/) - [HA PROXY](https://www.haproxy.com/) - [Caddy](https://caddyserver.com/) ## Getting the Real Client IP Address When your Fiber application is behind a reverse proxy, the TCP connection comes from the proxy server, not the actual client. To get the real client IP address, you need to configure Fiber to read it from proxy headers like `X-Forwarded-For`. :::warning Security Warning Proxy headers can be easily spoofed by malicious clients. **Always** configure `TrustProxyConfig` to validate the proxy IP address, otherwise attackers can forge headers to bypass IP-based access controls, rate limiting, or geolocation features. In addition, your reverse proxy should be configured to **set or overwrite** the forwarding header you choose (for example, `X-Forwarded-For`) based on the real client connection, or to use its real IP / PROXY protocol features. Do not simply pass through client-supplied forwarding headers, or `c.IP()` may still be controlled by an attacker even when `TrustProxyConfig` is correct. ::: :::info Chain parsing with `EnableIPValidation` `X-Forwarded-For` is a comma-separated chain that each proxy appends to. With `EnableIPValidation` enabled, `c.IP()` walks the chain from **right to left** and strips every IP that matches the configured `TrustProxyConfig`. The first non-trusted IP is returned as the client. This matches the [MDN guidance](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For#selecting_an_ip_address) and the convention used by Nginx (`set_real_ip_from` + `real_ip_recursive`), Apache `mod_remoteip`, Envoy (`xff_num_trusted_hops`), and most CDNs. Without `EnableIPValidation`, `c.IP()` returns the raw header value (a comma-separated string), which is rarely what middleware like rate limiters or allowlists expect. Enable validation when you rely on `c.IP()` as a single client identifier. ::: ### Configuration To enable reading the client IP from proxy headers, you must configure **three settings**: 1. **`TrustProxy`** - Enable proxy header trust (must be `true`) 2. **`ProxyHeader`** - Specify which header contains the client IP 3. **`TrustProxyConfig`** - Define which proxy IPs to trust ```go title="Example - App Behind Nginx" app := fiber.New(fiber.Config{ // Enable proxy support TrustProxy: true, // Read client IP from X-Forwarded-For header ProxyHeader: fiber.HeaderXForwardedFor, // Trust requests from your Nginx proxy TrustProxyConfig: fiber.TrustProxyConfig{ // Option 1: Trust specific proxy IPs Proxies: []string{"10.10.0.58", "192.168.1.0/24"}, // Option 2: Or trust all private IPs (useful for internal load balancers) // Private: true, }, }) ``` ### Common Proxy Headers Different proxies use different headers: | Proxy/Service | Recommended Header | Config Value | |---------------|-------------------|--------------| | Nginx, HAProxy, Apache | X-Forwarded-For | `fiber.HeaderXForwardedFor` | | Cloudflare | CF-Connecting-IP | `"CF-Connecting-IP"` | | Fastly | Fastly-Client-IP | `"Fastly-Client-IP"` | | Generic | X-Real-IP | `"X-Real-IP"` | ### TrustProxyConfig Options The `TrustProxyConfig` struct provides multiple ways to specify trusted proxies: ```go TrustProxyConfig: fiber.TrustProxyConfig{ // Specific IPs or CIDR ranges Proxies: []string{ "10.10.0.58", // Single IP "192.168.0.0/24", // CIDR range "2001:db8::/32", // IPv6 range }, // Or use convenience flags: Loopback: true, // Trust 127.0.0.0/8, ::1/128 LinkLocal: true, // Trust 169.254.0.0/16, fe80::/10 Private: true, // Trust 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7 UnixSocket: true, // Trust Unix domain socket connections }, ``` ### Complete Example with Nginx ```nginx title="nginx.conf" server { listen 443 ssl; http2 on; server_name example.com; ssl_certificate /etc/ssl/certs/example.crt; ssl_certificate_key /etc/ssl/private/example.key; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; # Overwrite untrusted inbound forwarding headers at the edge. proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; } } ``` ```go title="main.go" package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New(fiber.Config{ TrustProxy: true, ProxyHeader: fiber.HeaderXForwardedFor, EnableIPValidation: true, TrustProxyConfig: fiber.TrustProxyConfig{ // Trust localhost since Nginx is on the same machine Loopback: true, }, }) app.Get("/", func(c fiber.Ctx) error { // This will now return the real client IP from X-Forwarded-For // instead of 127.0.0.1 return c.SendString("Your IP: " + c.IP()) }) log.Fatal(app.Listen(":3000")) } ``` ### Testing Your Configuration You can verify your configuration is working: ```go app.Get("/debug", func(c fiber.Ctx) error { return c.JSON(fiber.Map{ "c.IP()": c.IP(), // Should show real client IP "X-Forwarded-For": c.Get("X-Forwarded-For"), // Raw header value "IsProxyTrusted": c.IsProxyTrusted(), // Should be true "RemoteIP": c.RequestCtx().RemoteIP().String(), // Proxy IP }) }) ``` ## Enabling HTTP/2 Popular choices include Nginx and Traefik.
Nginx Example See the [Complete Example with Nginx](#complete-example-with-nginx) above for a full configuration with HTTP/2 enabled.
Traefik Example ```yaml title="traefik.yaml" entryPoints: websecure: address: ":443" http: routers: app: rule: "Host(`example.com`)" entryPoints: - websecure service: app tls: {} services: app: loadBalancer: servers: - url: "http://127.0.0.1:3000" ``` With this configuration, Traefik terminates TLS and serves your app over HTTP/2.
## HTTP/3 (QUIC) Support Early Hints (103 responses) are defined for HTTP and can be delivered over HTTP/1.1 and HTTP/2/3. In practice, browsers process 103 most reliably over HTTP/2/3. Many reverse proxies also support HTTP/3 (QUIC): - **Nginx** - **Traefik** Enabling HTTP/3 is optional but can provide lower latency and improved performance for clients that support it. If you enable HTTP/3, your Early Hints responses will still work as expected. For more details, see the official documentation: - [Nginx QUIC / HTTP/3](https://nginx.org/en/docs/quic.html) - [Traefik HTTP/3](https://doc.traefik.io/traefik/reference/install-configuration/entrypoints/#http3) --- ## 🔌 Routing ## 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](#route-handlers), `"/users/:id"` is the [route path](#paths) (the resource, in REST terms) with `:id` a [route parameter](#parameters), and `func(c fiber.Ctx) error` is the [handler](#handler-types) (or [middleware](#middleware)) run when the route matches. Want to see matching happen? Fire requests at your own route table in the interactive [Route Matcher](../extra/route-matcher.md). ## Route Handlers Registers a route bound to a specific [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). The canonical handler is `func(fiber.Ctx) error`; Fiber also accepts `func(fiber.Ctx)` and runs it as if it returned `nil`. ```go title="Signatures" // HTTP methods func (app *App) Get(path string, handler any, handlers ...any) Router func (app *App) Head(path string, handler any, handlers ...any) Router func (app *App) Post(path string, handler any, handlers ...any) Router func (app *App) Put(path string, handler any, handlers ...any) Router func (app *App) Delete(path string, handler any, handlers ...any) Router func (app *App) Connect(path string, handler any, handlers ...any) Router func (app *App) Options(path string, handler any, handlers ...any) Router func (app *App) Trace(path string, handler any, handlers ...any) Router func (app *App) Patch(path string, handler any, handlers ...any) Router func (app *App) Query(path string, handler any, handlers ...any) Router // Add registers the same handlers on multiple methods at once. // The handlers run in order, starting with `handler` and then the variadic `handlers`. func (app *App) Add(methods []string, path string, handler any, handlers ...any) Router // All registers the route on every HTTP method at the EXACT path // (unlike Use, which is prefix-matched). func (app *App) All(path string, handler any, handlers ...any) Router ``` ```go title="Examples" // Simple GET handler app.Get("/api/list", func(c fiber.Ctx) error { return c.SendString("I'm a GET request!") }) // Simple POST handler app.Post("/api/register", func(c fiber.Ctx) error { return c.SendString("I'm a POST request!") }) ``` Here is a complete, runnable app for context: ```go title="A minimal Fiber app" 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](#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()`](../api/ctx.md#next) to continue the chain. ```go 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) ``` ```go 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) ``` ```go // 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 only ``` Multiple 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. ```go 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: handler ``` Attach several handlers in a single registration: list the route-specific middleware before the business handler. ```go 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](#constraints)) 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**. ```go // 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. ::: ## 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](../api/ctx.md#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). ```go // 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. ```go // 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](https://cloud.google.com/apis/design/custom_methods). Wrap routes in backticks to keep escape sequences clear. ```go // 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") }) ``` You can chain multiple named or unnamed parameters, including wildcard and plus segments, within a single segment. ```go // 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, each leading one captures a single character and the last captures the rest. This does not raise an error, so an unexpected pattern silently captures differently than you might assume. ::: 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](#constraints) 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](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0#route-constraints). :::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` | CD2C1638-1638-72D5-1638-DEADBEEF1638 | | float | `:weight` | 1.234, -1001.01e8, 3.14 | | minLen(value) | `:username` | Test (must be at least 4 characters) | | maxLen(value) | `:filename` | MyFile (must be no more than 8 characters) | | len(length) | `:filename` | somefile.txt (exactly 12 characters) | | min(value) | `:age` | 19 (Integer value must be at least 18) | | max(value) | `:age` | 91 (Integer value must be no more than 120) | | range(min,max) | `:age` | 91 (Integer value must be at least 18 but no more than 120) | | alpha | `:name` | Rick (String must consist of one or more alphabetical characters, a-z and case-insensitive) | | datetime | `:dob` | 2005-11-01 | | regex(expression) | `:date` | 2022-08-27 (Must match regular expression) | #### Examples ```go app.Get("/:test", 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 Found ``` You can use `;` for multiple constraints. ```go app.Get("/:test", 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 // 250 ``` Fiber precompiles the regex when registering routes, so the pattern is matched (not recompiled) on each request. ```go app.Get(`/:date`, 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. ```go 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 Found ``` #### Custom 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. ```go // 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: ```go 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. ```go // 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: ```go // 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](../extra/route-matcher.md) tool. ::: ## Middleware Functions that are designed to make changes to the request or response are called **middleware functions**. [`c.Next()`](../api/ctx.md#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. ```go title="Example of a middleware function" 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](#get-vs-use-vs-all) for how `Use` prefix matching differs from exact route matching, and how multiple handlers run in order. ### Use `Use` mounts middleware on a **prefix** (or **mount**) path: it runs for every request whose path begins with that prefix, on any HTTP method. Prefixes require either an exact match or a slash boundary, so `/john` matches `/john` and `/john/doe` but not `/johnnnnn`. Parameter tokens like `:name`, `:name?`, `*`, and `+` are still expanded before the boundary check runs. Called without a path, `Use` matches every request. ```go title="Signature" func (app *App) Use(args ...any) Router // Fiber inspects args to support these common usage patterns: // - app.Use(handler, handlers ...any) // - app.Use(path string, handler, handlers ...any) // - app.Use(paths []string, handler, handlers ...any) // - app.Use(path string, subApp *App) ``` Each handler argument can independently be a Fiber handler (with or without an `error` return), an Express-style callback, a `net/http` handler, or any other supported shape including fasthttp callbacks that return errors. ```go title="Examples" // Match any request app.Use(func(c fiber.Ctx) error { return c.Next() }) // Match request starting with /api app.Use("/api", func(c fiber.Ctx) error { return c.Next() }) // Match requests starting with /api or /home (multiple-prefix support) app.Use([]string{"/api", "/home"}, func(c fiber.Ctx) error { return c.Next() }) // Attach multiple handlers (they run in order; each must call c.Next() to continue) app.Use("/api", func(c fiber.Ctx) error { c.Set("X-Custom-Header", "value") return c.Next() }, func(c fiber.Ctx) error { return c.Next() }) // Mount a sub-app app.Use("/api", api) ``` ### 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`](../api/app.md#rebuildtree), [`RemoveRoute`](../api/app.md#removeroute), [`RemoveRouteByName`](../api/app.md#removeroutebyname), and [`RemoveRouteFunc`](../api/app.md#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`. ```go 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](./grouping.md). ### Route [`Route`](../api/app.md#route) is shorthand for [`Group`](#grouping): it scopes a set of routes under a common prefix declared inside a single callback, with an optional name prefix. ```go 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`](../api/app.md#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. ```go 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`](#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`](#route-handlers); one path with many methods (fluently) uses `RouteChain`; many paths under a shared prefix use [`Group`](#grouping) 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. ```go title="GET handlers automatically expose HEAD" 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: ```go title="Override the generated HEAD handler" app.Head("/users/:id", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) }) ``` To opt out globally, start the app with `DisableHeadAutoRegister`: ```go title="Disable automatic HEAD registration" 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 Registers a route bound to a specific [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). The canonical handler is `func(fiber.Ctx) error`; Fiber also accepts `func(fiber.Ctx)` and runs it as if it returned `nil`. ```go title="Signatures" // HTTP methods func (app *App) Get(path string, handler any, handlers ...any) Router func (app *App) Head(path string, handler any, handlers ...any) Router func (app *App) Post(path string, handler any, handlers ...any) Router func (app *App) Put(path string, handler any, handlers ...any) Router func (app *App) Delete(path string, handler any, handlers ...any) Router func (app *App) Connect(path string, handler any, handlers ...any) Router func (app *App) Options(path string, handler any, handlers ...any) Router func (app *App) Trace(path string, handler any, handlers ...any) Router func (app *App) Patch(path string, handler any, handlers ...any) Router func (app *App) Query(path string, handler any, handlers ...any) Router // Add registers the same handlers on multiple methods at once. // The handlers run in order, starting with `handler` and then the variadic `handlers`. func (app *App) Add(methods []string, path string, handler any, handlers ...any) Router // All registers the route on every HTTP method at the EXACT path // (unlike Use, which is prefix-matched). func (app *App) All(path string, handler any, handlers ...any) Router ``` ```go title="Examples" // Simple GET handler app.Get("/api/list", func(c fiber.Ctx) error { return c.SendString("I'm a GET request!") }) // Simple POST handler app.Post("/api/register", func(c fiber.Ctx) error { return c.SendString("I'm a POST request!") }) ``` --- ## 📝 Templates Templates render dynamic content without requiring a separate frontend framework. ## Template Engines Fiber accepts a custom template engine during app initialization. ```go app := fiber.New(fiber.Config{ // Provide a template engine Views: engine, // Default path for views, overridden when calling Render() ViewsLayout: "layouts/main", // Enables/Disables access to `ctx.Locals()` entries in rendered views // (defaults to false) PassLocalsToViews: false, }) ``` ### Supported Engines Fiber maintains a [templates](https://docs.gofiber.io/template) package that wraps several engines: * [ace](https://docs.gofiber.io/template/ace/) * [amber](https://docs.gofiber.io/template/amber/) * [django](https://docs.gofiber.io/template/django/) * [handlebars](https://docs.gofiber.io/template/handlebars) * [html](https://docs.gofiber.io/template/html) * [jet](https://docs.gofiber.io/template/jet) * [mustache](https://docs.gofiber.io/template/mustache) * [pug](https://docs.gofiber.io/template/pug) * [slim](https://docs.gofiber.io/template/slim) :::info Custom engines implement the `Views` interface to work with Fiber. ::: ```go title="Views interface" type Views interface { // Fiber executes Load() on app initialization to load/parse the templates Load() error // Outputs a template to the provided buffer using the provided template, // template name, and bound data Render(io.Writer, string, any, ...string) error } ``` :::note The `Render` method powers [**ctx.Render\(\)**](../api/ctx.md#render), which accepts a template name and data to bind. ::: ## Rendering Templates After configuring an engine, handlers call [**ctx.Render\(\)**](../api/ctx.md#render) with a template name and data to send the rendered output. ```go title="Signature" func (c Ctx) Render(name string, bind Map, layouts ...string) error ``` :::info By default, [**ctx.Render\(\)**](../api/ctx.md#render) searches for the template in the `ViewsLayout` path. Pass alternate paths in the `layouts` argument to override this behavior. ::: ```go app.Get("/", func(c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) ``` ```html

{{.Title}}

``` :::caution When `PassLocalsToViews` is enabled, all values set using `ctx.Locals(key, value)` are passed to the template. Use unique keys to avoid collisions. ::: ## Advanced Templating ### Custom Functions Fiber supports adding custom functions to templates. #### AddFunc Adds a global function to all templates. ```go title="Signature" func (e *Engine) AddFunc(name string, fn any) IEngineCore ``` ```go // Add `ToUpper` to engine engine := html.New("./views", ".html") engine.AddFunc("ToUpper", func(s string) string { return strings.ToUpper(s) } // Initialize Fiber App app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func (c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Content": "hello, World!" }) }) ``` ```html

This will be in {{ToUpper "all caps"}}:

{{ToUpper .Content}}

``` #### AddFuncMap Adds a Map of functions (keyed by name) to all templates. ```go title="Signature" func (e *Engine) AddFuncMap(m map[string]any) IEngineCore ``` ```go // Add `ToUpper` to engine engine := html.New("./views", ".html") engine.AddFuncMap(map[string]any{ "ToUpper": func(s string) string { return strings.ToUpper(s) }, }) // Initialize Fiber App app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func (c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Content": "hello, world!" }) }) ``` ```html

This will be in {{ToUpper "all caps"}}:

{{ToUpper .Content}}

``` * For more advanced template documentation, please visit the [gofiber/template GitHub Repository](https://github.com/gofiber/template). ## Full Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v2" ) func main() { // Initialize standard Go html template engine engine := html.New("./views", ".html") // If you want to use another engine, // just replace with following: // Create a new engine with django // engine := django.New("./views", ".django") app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index template return c.Render("index", fiber.Map{ "Title": "Go Fiber Template Example", "Description": "An example template", "Greeting": "Hello, World!", }); }) log.Fatal(app.Listen(":3000")) } ``` ```html {{.Title}}

{{.Title}}

{{.Greeting}}

``` --- ## 🧰 Utils ## 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. ```go title="Signature" func Convert[T any](value string, converter func(string) (T, error), defaultValue ...T) (T, error) ``` ```go title="Example" // 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. ```go title="Signature" func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) V ``` ```go title="Example" app.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. ```go title="Signature" // 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) V ``` ```go title="Example" app.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. ```go title="Signature" func Params[V GenericType](c Ctx, key string, defaultValue ...V) V ``` ```go title="Example" app.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. ```go title="Signature" func Query[V GenericType](c Ctx, key string, defaultValue ...V) V ``` ```go title="Example" app.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`. Set `StrictRouting: true` if you need the two forms to be distinguished. ```go title="Signature" func RoutePatternMatch(path, pattern string, cfg ...Config) bool ``` ```go title="Example" fiber.RoutePatternMatch("/user/john", "/user/:name") // true fiber.RoutePatternMatch( "/User/john", "/user/:name", fiber.Config{CaseSensitive: true}, ) // false ``` --- ## 🔎 Validation ## Validator package Fiber does not bundle a validation library. Instead, [`Bind`](../api/bind.md#validation) accepts any validator you plug into `fiber.Config.StructValidator` and runs it automatically whenever request data is bound onto a struct. The core stays dependency-free, and validation becomes a one-time setup instead of per-handler boilerplate. The steps below use [go-playground/validator](https://github.com/go-playground/validator), the most common choice in the Go ecosystem, but any library works as long as you wrap it in the small adapter from step 2. ### Step 1: Install a validator ```bash go get github.com/go-playground/validator/v10 ``` ### Step 2: Wire it into the app config `StructValidator` expects a single `Validate(out any) error` method, so wrap the library in a tiny adapter and register it once: ```go import "github.com/go-playground/validator/v10" type structValidator struct { validate *validator.Validate } // Validator needs to implement the Validate method func (v *structValidator) Validate(out any) error { return v.validate.Struct(out) } // Set up your validator in the config app := fiber.New(fiber.Config{ StructValidator: &structValidator{validate: validator.New()}, }) ``` :::note `StructValidator` runs only for struct destinations (or pointers to structs). Binding into maps and other non-struct types skips validation. ::: ### Step 3: Bind and validate in one call Tag your structs with the validator's rules. Every bind method (`Body`, `Query`, `Form`, and the rest) now triggers validation and returns its errors alongside binding errors: ```go type User struct { Name string `json:"name" form:"name" query:"name" validate:"required"` Age int `json:"age" form:"age" query:"age" validate:"gte=0,lte=100"` } app.Post("/", func(c fiber.Ctx) error { user := new(User) // Works with all bind methods: Body, Query, Form, ... if err := c.Bind().Body(user); err != nil { // validation errors are returned here return err } return c.JSON(user) }) ``` Returned as-is, a validation error surfaces through Fiber's [error handling](./error-handling.md), so you can also shape it globally in a custom error handler. ### Step 4: Shape the error response For field-level feedback, unwrap `validator.ValidationErrors` and answer with a structured body instead of the default text: ```go type User struct { Name string `json:"name" validate:"required,min=3,max=32"` Email string `json:"email" validate:"required,email"` Age int `json:"age" validate:"gte=0,lte=100"` Password string `json:"password" validate:"required,min=8"` Website string `json:"website" validate:"url"` } app.Post("/user", func(c fiber.Ctx) error { user := new(User) if err := c.Bind().Body(user); err != nil { var validationErrors validator.ValidationErrors if errors.As(err, &validationErrors) { out := make([]fiber.Map, 0, len(validationErrors)) for _, e := range validationErrors { // e.Field() - field name, e.Tag() - failed rule, // e.Param() - rule parameter, e.Value() - invalid value out = append(out, fiber.Map{ "field": e.Field(), "rule": e.Tag(), }) } return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"errors": out}) } return err } return c.JSON(user) }) ``` ### Step 5: Add your own rules Because the adapter is yours, you can layer custom checks on top of the tag rules: ```go // Custom validator for password strength type PasswordValidator struct { validate *validator.Validate } func (v *PasswordValidator) Validate(out any) error { if err := v.validate.Struct(out); err != nil { return err } // Custom password validation logic if user, ok := out.(*User); ok { if len(user.Password) < 8 { return errors.New("password must be at least 8 characters") } // Add more password validation rules here } return nil } // Usage app := fiber.New(fiber.Config{ StructValidator: &PasswordValidator{validate: validator.New()}, }) ``` --- ## 👋 Welcome(Core) Welcome to Fiber's online API documentation, complete with examples to help you start building web applications right away! **Fiber** is an [Express](https://github.com/expressjs/express)-inspired **web framework** built on top of [Fasthttp](https://github.com/valyala/fasthttp), the **fastest** HTTP engine for [Go](https://go.dev/doc/). It is designed to facilitate rapid development with **zero memory allocations** and a strong focus on **performance**. Fiber also ships batteries included: built-in middleware, officially maintained integrations, storage drivers, and template engines cover most production needs (see [Explore the Ecosystem](#explore-the-ecosystem) below). These docs cover **Fiber v3**. :::tip Coming from Fiber v2? See [What's New in v3](./whats_new.md) for the migration guide and the CLI migration tool. ::: ## Installation First, [download](https://go.dev/dl/) and install Go. Version `1.25` or higher is required. Install Fiber using the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: ```bash go get github.com/gofiber/fiber/v3 ``` ## Hello, World Create a file named `server.go` with the simplest **Fiber** application you can write: ```go package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) log.Fatal(app.Listen(":3000")) } ``` Run it: ```bash go run server.go ``` Browse to `http://localhost:3000` and you should see `Hello, World!` displayed on the page. Three calls carry the whole program: - `fiber.New()` creates the app, the central object that holds routes, middleware, and configuration - `app.Get("/", ...)` registers a handler: a function that receives the request context `c fiber.Ctx` and returns an `error` - `app.Listen(":3000")` starts the server ## Build Your First App, Step by Step The walkthrough below grows the Hello, World program into a small app with route parameters, static files, middleware, a JSON endpoint, and error handling. Step through it and fire the example requests to see exactly how the app responds at every stage: ## What Can You Build? Fiber covers the everyday shapes of web backends out of the box. Pick a use case and read the code; every tab is just complete enough to recognize your project in it: Want complete, runnable projects instead? The [Recipes collection](https://docs.gofiber.io/recipes/) has working examples for Docker, GORM, JWT auth, clean architecture, and much more. ## Basic Routing Routing determines how an application responds to a client request at a particular endpoint, a combination of path and HTTP request method (`GET`, `PUT`, `POST`, etc.). Route definitions follow the structure below: ```go // Function signature func (app *App) Get(path string, handler any, handlers ...any) Router ``` - `app` is an instance of **Fiber** - `Get` is an [HTTP request method](./api/app.md#route-handlers); `Post`, `Put`, `Delete`, and the other methods work the same way - `path` is a virtual path on the server - `handler` is a function executed when the route is matched; the canonical form is `func(fiber.Ctx) error`, and a route can register multiple handlers For an interactive breakdown of every part of a route definition, see the [anatomy of a route](./guide/routing.md#anatomy-of-a-route). A simple route and a route with a parameter: ```go // Respond with "Hello, World!" on root path "/" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ``` ```go // GET http://localhost:3000/hello%20world app.Get("/:value", func(c fiber.Ctx) error { return c.SendString("value: " + c.Params("value")) // => Response: "value: hello world" }) ``` See the [routing guide](./guide/routing.md) for optional parameters, wildcards, constraints, route groups, and the full list of supported handler types. Or skip ahead and fire requests at your own route table in the interactive [Route Matcher](./extra/route-matcher.md). ## Static Files To serve static files such as **images**, **CSS**, and **JavaScript** files, register the [static middleware](./middleware/static.md): ```go import "github.com/gofiber/fiber/v3/middleware/static" app.Use("/", static.New("./public")) ``` Files in the `./public` directory are now reachable in the browser, for example at `http://localhost:3000/css/style.css`. ## Using Middleware Middleware runs before or after your handlers and takes care of cross-cutting concerns. Registering one is a single `app.Use` call; here is the [logger](./middleware/logger.md) middleware printing every request: ```go import "github.com/gofiber/fiber/v3/middleware/logger" app.Use(logger.New()) ``` Middleware for logging, CORS, rate limiting, sessions, compression, panic [recovery](./middleware/recover.md), and much more ships with Fiber itself; the ecosystem section below shows where to find it all. ## Zero Allocation :::caution Fiber is optimized for **high performance**, so values returned from **fiber.Ctx** are **not** immutable by default and **will** be reused across requests. Use context values only within the handler, and do not keep any references after the handler returns. ::: If you need to persist a context value beyond the handler, make a copy of its **underlying buffer** using the [copy](https://pkg.go.dev/builtin/#copy) builtin: ```go func handler(c fiber.Ctx) error { // Variable is only valid within this handler result := c.Params("foo") // Make a copy buffer := make([]byte, len(result)) copy(buffer, result) resultCopy := string(buffer) // Variable is now valid indefinitely // ... } ``` Alternatively, you can enable the `Immutable` setting. This makes all values returned from the context immutable, allowing you to persist them anywhere, at the cost of some performance: ```go app := fiber.New(fiber.Config{ Immutable: true, }) ``` For details, see [Immutable](./api/fiber.md#immutable) in the configuration reference and the [GetString and GetBytes](./api/app.md#getstring) helpers. ## Explore the Ecosystem Fiber is more than the core module. When your application grows, these officially maintained building blocks are one import away: - **Built-in middleware**: 30+ middleware for logging, CORS, security headers, caching, rate limiting, and more live in the core module; browse the [middleware overview](https://docs.gofiber.io/category/-middleware). - **[Contrib packages](https://docs.gofiber.io/contrib/)**: officially maintained integrations with external dependencies, such as JWT, WebSocket, OpenTelemetry, Casbin, and structured logging adapters. - **[Storage drivers](https://docs.gofiber.io/storage/)**: a growing list of backends (Redis, Postgres, MongoDB, S3, and more) behind one interface, ready to plug into the session, limiter, cache, and idempotency middleware. - **[Template engines](https://docs.gofiber.io/template/)**: server-side rendering through the Views interface, with engines like html, django, handlebars, and pug. - **[HTTP client](./client/rest.md)**: a built-in client, also built on Fasthttp, for calling other services with the same performance philosophy. - **[Recipes](https://docs.gofiber.io/recipes/)**: runnable example projects (Docker, GORM, JWT auth, clean architecture, and more) to copy a working starting point from. For a visual map of how these repositories plug into each other, see the [Ecosystem overview](./ecosystem.md). ## Next Steps Work through these in order and you will have touched everything a typical production service needs: 1. [Routing](./guide/routing.md): parameters, wildcards, and constraints; try them live in the [Route Matcher](./extra/route-matcher.md) 2. [Grouping](./guide/grouping.md) and the [middleware catalog](https://docs.gofiber.io/category/-middleware): structure the app and its cross-cutting concerns 3. [Error handling](./guide/error-handling.md): central error handlers and status codes 4. [Request binding](./api/bind.md) and [validation](./guide/validation.md): map request data onto structs safely 5. [Templates](./guide/templates.md): render views with your favorite template engine 6. [HTTP client](./client/rest.md): call other services with the same performance philosophy 7. [Performance](./guide/faster-fiber.md): custom JSON encoders and the tricks behind the benchmarks 8. [Testing](./api/app.md#test): test handlers without a running server using `app.Test` The [configuration reference](./api/fiber.md) lists every option accepted by `fiber.New`, and the [learning resources](./extra/learning-resources.md) page collects tutorials and hands-on challenges. ## Community and Help Stuck or have questions? Join the [Discord](https://gofiber.io/discord) server or check the [FAQ](./extra/faq.md). Fiber is developed in the open on [GitHub](https://github.com/gofiber/fiber); issues, discussions, and contributions are welcome. --- ## Adaptor The `adaptor` package converts between Fiber and `net/http`, letting you reuse handlers, middleware, and requests across both frameworks. :::tip Fiber can register plain `net/http` handlers directly—just pass an `http.Handler`, `http.HandlerFunc`, or `func(http.ResponseWriter, *http.Request)` to any router method and it will be adapted automatically. The adaptor helpers remain valuable when you need to convert middleware, swap handler directions, or transform requests explicitly. ::: :::caution Fiber features are unavailable Even when you register them directly, adapted `net/http` handlers still run with standard library semantics. They don't have access to `fiber.Ctx`, and the compatibility layer comes with additional overhead compared to native Fiber handlers. Use them for interop and legacy scenarios, but prefer Fiber handlers when performance or Fiber-specific APIs matter. ::: ## Features - Convert `net/http` handlers and middleware to Fiber handlers - Convert Fiber handlers to `net/http` handlers - Convert a Fiber context (`fiber.Ctx`) into an `http.Request` - Copy values stored in a `context.Context` onto a `fasthttp.RequestCtx` :::note Body size limits when running Fiber from net/http When Fiber is executed from a `net/http` server through `FiberHandler`, `FiberHandlerFunc`, or `FiberApp`, the adaptor enforces the app's configured `BodyLimit`. The app's `BodyLimit` defaults to **4 MiB** if a non-positive value is provided during configuration. Requests exceeding the active limit receive `413 Request Entity Too Large`. ::: ## API Reference | Name | Signature | Description | |-------------------------------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------| | `HTTPHandler` | `HTTPHandler(h http.Handler) fiber.Handler` | Converts `http.Handler` to `fiber.Handler` | | `HTTPHandlerWithContext` | `HTTPHandlerWithContext(h http.Handler) fiber.Handler` | Converts `http.Handler` to `fiber.Handler`, propagating Fiber's local context | | `HTTPHandlerFunc` | `HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler` | Converts `http.HandlerFunc` to `fiber.Handler` | | `HTTPMiddleware` | `HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler` | Converts `http.Handler` middleware to `fiber.Handler` middleware | | `FiberHandler` | `FiberHandler(h fiber.Handler) http.Handler` | Converts `fiber.Handler` to `http.Handler` | | `FiberHandlerFunc` | `FiberHandlerFunc(h fiber.Handler) http.HandlerFunc` | Converts `fiber.Handler` to `http.HandlerFunc` | | `FiberApp` | `FiberApp(app *fiber.App) http.HandlerFunc` | Converts an entire Fiber app to a `http.HandlerFunc` | | `ConvertRequest` | `ConvertRequest(c fiber.Ctx, forServer bool) (*http.Request, error)` | Converts `fiber.Ctx` into a `http.Request` | | `LocalContextFromHTTPRequest` | `LocalContextFromHTTPRequest(r *http.Request) (context.Context, bool)` | Extracts the propagated `context.Context` from an adapted `http.Request` | | `CopyContextToFiberContext` | `CopyContextToFiberContext(context any, requestContext *fasthttp.RequestCtx)` | Copies `context.Context` to `fasthttp.RequestCtx` | --- ## Usage Examples ### 1. Using `net/http` handlers in Fiber (`HTTPHandler`, `HTTPHandlerFunc`) Run standard `net/http` handlers inside Fiber. Fiber can auto-adapt them, or you can explicitly convert them when you want to cache or share the converted handler. ```go package main import ( "fmt" "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { app := fiber.New() // Fiber adapts net/http handlers for you during registration. app.Get("/", http.HandlerFunc(helloHandler)) // You can also convert and reuse the handler manually. cached := adaptor.HTTPHandler(http.HandlerFunc(helloHandler)) app.Get("/cached", cached) // When you already have an http.HandlerFunc, convert it directly. app.Get("/func", adaptor.HTTPHandlerFunc(helloHandler)) app.Listen(":3000") } func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello from net/http!") } ``` ### 2. Using `net/http` middleware with Fiber (`HTTPMiddleware`) Middleware written for `net/http` can run inside Fiber: ```go package main import ( "log" "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { app := fiber.New() // Apply an http middleware in Fiber app.Use(adaptor.HTTPMiddleware(loggingMiddleware)) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello Fiber!") }) app.Listen(":3000") } func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("Request received") next.ServeHTTP(w, r) }) } ``` ### 3. Using Fiber handlers in `net/http` (`FiberHandler`) You can use Fiber handlers from `net/http`: ```go package main import ( "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { // Convert a Fiber handler to an http.Handler http.Handle("/", adaptor.FiberHandler(helloFiber)) // Convert a Fiber handler to an http.HandlerFunc http.HandleFunc("/func", adaptor.FiberHandlerFunc(helloFiber)) http.ListenAndServe(":3000", nil) } func helloFiber(c fiber.Ctx) error { return c.SendString("Hello from Fiber!") } ``` ### 4. Converting Fiber handlers to `http.HandlerFunc` (`FiberHandlerFunc`) When you specifically need an `http.HandlerFunc`, wrap the Fiber handler directly: ```go package main import ( "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { http.HandleFunc("/func-only", adaptor.FiberHandlerFunc(helloFiber)) http.ListenAndServe(":3000", nil) } func helloFiber(c fiber.Ctx) error { return c.SendString("Hello from Fiber!") } ``` ### 5. Running a full Fiber app inside `net/http` (`FiberApp`) You can wrap a full Fiber app inside `net/http`: ```go package main import ( "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello from Fiber!") }) // Run Fiber inside an http server http.ListenAndServe(":3000", adaptor.FiberApp(app)) } ``` ### 6. Converting `fiber.Ctx` to `*http.Request` (`ConvertRequest`) Create an `*http.Request` from a `fiber.Ctx`. The `forServer` parameter determines how server-oriented fields are populated: - Use `forServer = true` when the converted request will be passed into a `net/http` handler (sets `RequestURI`, `RemoteAddr`, and `TLS` fields for server-side handling) - Use `forServer = false` when creating a request for client-side use (e.g., making an outbound HTTP request with `http.Client`) ```go package main import ( "net/http" "net/http/httptest" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { app := fiber.New() app.Get("/request", handleRequest) app.Listen(":3000") } func handleRequest(c fiber.Ctx) error { // Use forServer = true when passing to a net/http handler httpReq, err := adaptor.ConvertRequest(c, true) if err != nil { return err } // Pass the request to a net/http handler. recorder := httptest.NewRecorder() http.DefaultServeMux.ServeHTTP(recorder, httpReq) return c.SendString("Converted Request URL: " + httpReq.URL.String()) } ``` ### 7. Passing Fiber user context into `net/http` This example shows a realistic flow: a Fiber middleware sets a request-scoped `context.Context` (with a `request_id`) on the Fiber context, then an adapted `net/http` handler retrieves it via `LocalContextFromHTTPRequest`. ```go package main import ( "context" "fmt" "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) type ctxKey string const requestIDKey ctxKey = "request_id" func main() { app := fiber.New() // Create a request-scoped context in Fiber (e.g., request id, auth claims, trace span). app.Use(func(c fiber.Ctx) error { reqID := c.Get("X-Request-ID") ctx := context.WithValue(context.Background(), requestIDKey, reqID) // Fiber stores request-scoped context as "user context". c.SetContext(ctx) return c.Next() }) // 2) Run a standard net/http handler that includes Fiber's user context propagated. app.Get("/hello", adaptor.HTTPHandlerWithContext(http.HandlerFunc(handleRequest))) app.Listen(":3000") } func handleRequest(w http.ResponseWriter, r *http.Request) { ctx, ok := adaptor.LocalContextFromHTTPRequest(r) if !ok || ctx == nil { http.Error(w, "missing propagated context", http.StatusInternalServerError) return } reqID, _ := ctx.Value(requestIDKey).(string) fmt.Fprintf(w, "Hello from net/http (request_id=%s)\n", reqID) } ``` ### 8. Copying context values onto `fasthttp.RequestCtx` (`CopyContextToFiberContext`) `CopyContextToFiberContext` copies values stored in a `context.Context` onto a `fasthttp.RequestCtx`. The function is marked deprecated in code because it uses reflection and unsafe operations—prefer explicit parameter passing when possible. When you do need it, call it immediately after you add values to the `net/http` context so Fiber can read them via `c.Context()`: ```go package main import ( "context" "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/adaptor" ) type contextKey string func main() { app := fiber.New() app.Use(func(c fiber.Ctx) error { // Convert the Fiber context to an http.Request so we can attach context values. httpReq, err := adaptor.ConvertRequest(c, true) if err != nil { return err } // Add context data and push it back to the Fiber context. enriched := httpReq.WithContext(context.WithValue(httpReq.Context(), contextKey("requestID"), "req-123")) adaptor.CopyContextToFiberContext(enriched.Context(), c.RequestCtx()) return c.Next() }) app.Get("/", func(c fiber.Ctx) error { if id, ok := c.Context().Value(contextKey("requestID")).(string); ok { return c.SendString("Request ID: " + id) } return c.SendStatus(fiber.StatusNotFound) }) app.Listen(":3000") } ``` --- ## Summary The `adaptor` package lets Fiber and `net/http` interoperate so you can: - Convert handlers and middleware in both directions - Run Fiber apps inside `net/http` - Convert `fiber.Ctx` to `http.Request` - Propagate Fiber's user context into adapted `net/http` handlers This makes it straightforward to integrate Fiber with existing Go projects or migrate between frameworks. --- ## BasicAuth Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides HTTP basic auth. It calls the next handler for valid credentials and returns [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) for missing or invalid credentials, [`400 Bad Request`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) for malformed `Authorization` headers, or [`431 Request Header Fields Too Large`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) when the header exceeds size limits. Credentials may omit Base64 padding as permitted by RFC 7235's `token68` syntax. The default unauthorized response includes the header `WWW-Authenticate: Basic realm="Restricted", charset="UTF-8"`, sets `Cache-Control: no-store`, and adds a `Vary: Authorization` header. Only the `UTF-8` charset is supported; any other value will panic. ## Signatures ```go func New(config Config) fiber.Handler func UsernameFromContext(ctx any) string ``` `UsernameFromContext` accepts a `fiber.CustomCtx`, `fiber.Ctx`, a `*fasthttp.RequestCtx`, or a `context.Context`. ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/basicauth" ) ``` Once your Fiber app is initialized, choose one of the following approaches: ```go // Provide a minimal config app.Use(basicauth.New(basicauth.Config{ Users: map[string]string{ // "doe" hashed using SHA-256 "john": "{SHA256}eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=", // "123456" hashed using bcrypt "admin": "$2a$10$gTYwCN66/tBRoCr3.TXa1.v1iyvwIF7GRBqxzv7G.AHLMt/owXrp.", }, })) // Or extend your config for customization app.Use(basicauth.New(basicauth.Config{ Users: map[string]string{ // "doe" hashed using SHA-256 "john": "{SHA256}eZ75KhGvkY4/t0HfQpNPO1aO0tk6wd908bjUGieTKm8=", // "123456" hashed using bcrypt "admin": "$2a$10$gTYwCN66/tBRoCr3.TXa1.v1iyvwIF7GRBqxzv7G.AHLMt/owXrp.", }, Realm: "Forbidden", Authorizer: func(user, pass string, c fiber.Ctx) bool { // custom validation logic return (user == "john" || user == "admin") }, Unauthorized: func(c fiber.Ctx) error { return c.SendFile("./unauthorized.html") }, })) ``` ### Password hashes Passwords must be supplied in pre-hashed form. The middleware detects the hashing algorithm from a prefix: - `"{SHA512}"` or `"{SHA256}"` followed by a base64-encoded digest - standard bcrypt strings beginning with `$2` If no prefix is present, the value is interpreted as a SHA-256 digest encoded in hex or base64. Plaintext passwords are rejected. The decoded digest must be exactly the size of the named algorithm — 32 bytes for SHA-256, 64 bytes for SHA-512. A digest of any other length could never match a password, so it is rejected at startup: `New()` panics with `ErrInvalidSHA256PasswordLength` or `ErrInvalidSHA512PasswordLength` rather than starting with an account that can never authenticate. The most common cause is a truncated copy/paste, or a SHA-256 digest stored under the `{SHA512}` prefix. #### Generating SHA-256 and SHA-512 passwords Create a digest, encode it in base64, and prefix it with `{SHA256}` or `{SHA512}` before adding it to `Users`: ```bash # SHA-256 printf 'secret' | openssl dgst -binary -sha256 | base64 # SHA-512 printf 'secret' | openssl dgst -binary -sha512 | base64 ``` Include the prefix in your config: ```go Users: map[string]string{ "john": "{SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=", "admin": "{SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==", } ``` ## Config | Property | Type | Description | Default | |:----------------|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Users | `map[string]string` | Users maps usernames to **hashed** passwords (e.g. bcrypt, `{SHA256}`). | `map[string]string{}` | | Realm | `string` | Realm is a string to define the realm attribute of BasicAuth. The realm identifies the system to authenticate against and can be used by clients to save credentials. | `"Restricted"` | | Charset | `string` | Charset sent in the `WWW-Authenticate` header. Only `"UTF-8"` is supported (case-insensitive). | `"UTF-8"` | | HeaderLimit | `int` | Maximum allowed length of the `Authorization` header. Requests exceeding this limit are rejected. | `8192` | | Authorizer | `func(string, string, fiber.Ctx) bool` | Authorizer defines a function to check the credentials. It will be called with a username, password, and the current context and is expected to return true or false to indicate approval. | `nil` | | Unauthorized | `fiber.Handler` | Unauthorized defines the response body for unauthorized responses. | `nil` | | BadRequest | `fiber.Handler` | BadRequest defines the response for malformed `Authorization` headers. | `nil` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Users: map[string]string{}, Realm: "Restricted", Charset: "UTF-8", HeaderLimit: 8192, Authorizer: nil, Unauthorized: nil, BadRequest: nil, } ``` --- ## Cache Cache middleware for [Fiber](https://github.com/gofiber/fiber) that intercepts responses and stores the body, `Content-Type`, and status code under a deterministic key derived from request dimensions. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for contributing this middleware to Fiber core. By default, cached responses expire after five minutes and the middleware stores up to 1 MB of response bodies. ## Request directives - `Cache-Control: no-cache` returns the latest response while still caching it, so the status is always `miss`. - `Cache-Control: no-store` skips caching and always forwards a fresh response. If the response includes a `Cache-Control: max-age` directive, its value sets the cache entry's expiration. ## Cacheable status codes The middleware caches these RFC 7231 status codes: - `200: OK` - `203: Non-Authoritative Information` - `204: No Content` - `206: Partial Content` - `300: Multiple Choices` - `301: Moved Permanently` - `404: Not Found` - `405: Method Not Allowed` - `410: Gone` - `414: URI Too Long` - `501: Not Implemented` Responses with other status codes result in an `unreachable` cache status. For more about cacheable status codes and RFC 7231, see: - [Cacheable - MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/Cacheable) - [RFC7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content](https://datatracker.ietf.org/doc/html/rfc7231) ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/cache" "github.com/gofiber/utils/v2" ) ``` Once your Fiber app is initialized, register the middleware: ```go // Initialize default config app.Use(cache.New()) // Or extend the config for customization app.Use(cache.New(cache.Config{ Next: func(c fiber.Ctx) bool { return fiber.Query[bool](c, "noCache") }, Expiration: 30 * time.Minute, DisableCacheControl: true, })) ``` Customize expiration and cache key behavior: ```go app.Use(cache.New(cache.Config{ ExpirationGenerator: func(c fiber.Ctx, cfg *cache.Config) time.Duration { newCacheTime, _ := strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) return time.Second * time.Duration(newCacheTime) }, // Optional: fully custom key KeyGenerator: func(c fiber.Ctx) string { return utils.CopyString(c.Path()) + "|tenant=" + c.Get("X-Tenant-ID") }, })) app.Get("/", func(c fiber.Ctx) error { c.Response().Header.Add("Cache-Time", "6000") return c.SendString("hi") }) ``` Use `CacheInvalidator` to invalidate entries programmatically: ```go app.Use(cache.New(cache.Config{ CacheInvalidator: func(c fiber.Ctx) bool { return fiber.Query[bool](c, "invalidateCache") }, })) ``` `CacheInvalidator` defines custom invalidation rules. Return `true` to bypass the cache. In the example above, setting the `invalidateCache` query parameter to `true` invalidates the entry. Cache keys are masked in logs and error messages by default. Set `DisableValueRedaction` to `true` if you explicitly need the raw key for debugging. ### Default cache key behavior (safe by default) By default, cache keys include: - request method (partitioned internally by the middleware), - request path, - canonicalized query string (enabled unless `DisableQueryKeys` is `true`), - representation-driving request headers (`accept`, `accept-encoding`, `accept-language`). This prevents common collisions from path-only keys (for example, `/?id=1` vs `/?id=2`) while keeping fragmentation bounded. The middleware **does not include request body/form values in the default cache key**, except for `QUERY` requests: per [RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html), when `QUERY` is enabled via `Methods` the default key generator incorporates the request body so different bodies on the same URL get distinct keys. Cache lookup/storage is applied only for `GET` and `HEAD` requests by default. Other HTTP methods bypass the cache middleware. You can change this via the `Methods` config field (for example, adding `fiber.MethodQuery`). If you supply a custom `KeyGenerator` and enable a body-bearing method such as `QUERY`, make sure it incorporates `c.Request().Body()`, otherwise requests with the same URL but different bodies will collide. If a response sets `Vary`, request lookup/storage is also partitioned by those header values unless `DisableVaryHeaders` is `true`. Responses with `Vary: *` remain uncacheable. ## Config | Property | Type | Description | Default | | :------------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------- | | Next | `func(fiber.Ctx) bool` | Next defines a function that is executed before creating the cache entry and can be used to execute the request without cache creation. If an entry already exists, it will be used. If you want to completely bypass the cache functionality in certain cases, you should use the [skip middleware](skip.md). | `nil` | | Expiration | `time.Duration` | Expiration is the time that a cached response will live. | `5 * time.Minute` | | CacheHeader | `string` | CacheHeader is the header on the response header that indicates the cache status, with the possible return values "hit," "miss," or "unreachable." | `X-Cache` | | DisableCacheControl | `bool` | DisableCacheControl omits the `Cache-Control` header when set to `true`. | `false` | | CacheInvalidator | `func(fiber.Ctx) bool` | CacheInvalidator defines a function that is executed before checking the cache entry. It can be used to invalidate the existing cache manually by returning true. | `nil` | | DisableValueRedaction | `bool` | Turns off cache key redaction in logs and error messages when set to `true`. | `false` | | KeyGenerator | `func(fiber.Ctx) string` | KeyGenerator allows you to generate custom keys. The HTTP method is partitioned internally by the middleware. | structured key from path + canonical query + selected headers/cookies | | DisableQueryKeys | `bool` | Disables canonicalized query params in keys. | `false` | | KeyHeaders | `[]string` | Header allow-list used for key partitioning. Names are normalized case-insensitively and sorted. Use `[]string{}` to disable header-based partitioning. | `[]string{"accept","accept-encoding","accept-language"}` | | KeyCookies | `[]string` | Optional cookie allow-list for key partitioning. Explicit opt-in only; names remain case-sensitive. | `nil` | | Methods | `[]string` | HTTP methods eligible for caching. Requests whose method is not in this list bypass the cache. Names are normalized to uppercase. | `[]string{fiber.MethodGet, fiber.MethodHead}` | | DisableVaryHeaders | `bool` | Disables response `Vary` dimensions in cache lookup/storage partitioning. | `false` | | ExpirationGenerator | `func(fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | | Storage | `fiber.Storage` | Storage is used to store the state of the middleware. | In-memory store | | StoreResponseHeaders | `bool` | StoreResponseHeaders allows you to store additional headers generated by next middlewares & handler. | `false` | | MaxBytes | `uint` | MaxBytes is the maximum number of bytes of response bodies simultaneously stored in cache. | `1 * 1024 * 1024` (~1 MB) | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Expiration: 5 * time.Minute, CacheHeader: "X-Cache", DisableCacheControl: false, CacheInvalidator: nil, DisableValueRedaction: false, KeyGenerator: nil, // uses structured default key generator DisableQueryKeys: false, KeyHeaders: []string{ fiber.HeaderAccept, fiber.HeaderAcceptEncoding, fiber.HeaderAcceptLanguage, }, KeyCookies: nil, Methods: []string{fiber.MethodGet, fiber.MethodHead}, DisableVaryHeaders: false, ExpirationGenerator: nil, StoreResponseHeaders: false, Storage: nil, MaxBytes: 1 * 1024 * 1024, } ``` --- ## Compress Compression middleware for [Fiber](https://github.com/gofiber/fiber) that automatically compresses responses with `gzip`, `deflate`, `brotli`, or `zstd` based on the client's [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. :::note Bodies smaller than 200 bytes remain uncompressed because compression would likely increase their size and waste CPU cycles. [See the fasthttp source](https://github.com/valyala/fasthttp/blob/497922a21ef4b314f393887e9c6147b8c3e3eda4/http.go#L1713-L1715). ::: ## Behavior - Skips compression for responses that already define `Content-Encoding`, for range requests, `206` responses, status codes without bodies, or when either side sends `Cache-Control: no-transform`. - `HEAD` requests negotiate compression so `Content-Encoding`, `Content-Length`, `ETag`, and `Vary` reflect the encoded representation, but the body is removed before sending. - When compression runs, strong `ETag` values are recomputed from the compressed bytes; when skipped, `Accept-Encoding` is still merged into `Vary` unless the header is `*` or already present. - Request-body decompression is still handled by Fiber's request APIs (for example `c.Body()`), and those decoders enforce the app `BodyLimit` for compressed payloads. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/compress" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(compress.New()) // Or extend your config for customization app.Use(compress.New(compress.Config{ Level: compress.LevelBestSpeed, // 1 })) // Skip middleware for specific routes app.Use(compress.New(compress.Config{ Next: func(c fiber.Ctx) bool { return c.Path() == "/dont_compress" }, Level: compress.LevelBestSpeed, // 1 })) ``` ## Config | Property | Type | Description | Default | |:-------- |:-----------------------|:------------------------------------------------------------|:-------------------| | Next | `func(fiber.Ctx) bool` | Skips this middleware when the function returns `true`. | `nil` | | Level | `Level` | Compression level to use. | `LevelDefault (0)` | Possible values for the "Level" field are: - `LevelDisabled (-1)`: Compression is disabled. - `LevelDefault (0)`: Default compression level. - `LevelBestSpeed (1)`: Best compression speed. - `LevelBestCompression (2)`: Best compression. ## Default Config ```go var ConfigDefault = Config{ Next: nil, Level: LevelDefault, } ``` ## Constants ```go // Compression levels const ( LevelDisabled = -1 LevelDefault = 0 LevelBestSpeed = 1 LevelBestCompression = 2 ) ``` --- ## CORS CORS (Cross-Origin Resource Sharing) middleware for [Fiber](https://github.com/gofiber/fiber) lets servers control who can access resources and how. It isn't a security feature; it merely relaxes the browser's same-origin policy so cross-origin requests can succeed. Learn more on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). It adds CORS headers to responses, listing allowed origins, methods, and headers, and handles preflight checks. Use the `AllowOrigins` option to define which origins may send cross-origin requests. It accepts single origins, lists, subdomain patterns, wildcards, and supports dynamic validation with `AllowOriginsFunc`. The middleware normalizes `AllowOrigins`, verifies HTTP/HTTPS schemes, and strips trailing slashes. Invalid origins cause a panic. Panic messages and logs redact misconfigured origins by default; set `DisableValueRedaction` to `true` if you need the raw value for troubleshooting. Avoid [common pitfalls](#common-pitfalls) such as using wildcard origins with credentials, overly permissive origin lists, or skipping validation with `AllowOriginsFunc`, as misconfiguration can create security risks. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/cors" ) ``` Once your Fiber app is initialized, apply the middleware in one of the following ways: ### Basic usage To use the default configuration, simply use `cors.New()`. This will allow wildcard origins '*', all methods, no credentials, and no headers or exposed headers. ```go app.Use(cors.New()) ``` ### Custom configuration (specific origins, headers, etc.) ```go // Initialize default config app.Use(cors.New()) // Or extend your config for customization app.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://gofiber.io", "https://gofiber.net"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept"}, })) ``` ### Dynamic origin validation You can use `AllowOriginsFunc` to programmatically determine whether to allow a request based on its origin. This is useful when you need to validate origins against a database or other dynamic sources. The function should return `true` if the origin is allowed, and `false` otherwise. Be sure to review the [security considerations](#security-considerations) when using `AllowOriginsFunc`. :::caution Never allow `AllowOriginsFunc` to return `true` for all origins. This is particularly crucial when `AllowCredentials` is set to `true`. Doing so can bypass the restriction of using a wildcard origin with credentials, exposing your application to serious security threats. If you need to allow wildcard origins, use `AllowOrigins` with a wildcard `"*"` instead of `AllowOriginsFunc`. ::: ```go // dbCheckOrigin checks if the origin is in the list of allowed origins in the database. func dbCheckOrigin(db *sql.DB, origin string) bool { // Placeholder query - adjust according to your database schema and query needs query := "SELECT COUNT(*) FROM allowed_origins WHERE origin = $1" var count int err := db.QueryRow(query, origin).Scan(&count) if err != nil { // Handle error (e.g., log it); for simplicity, we return false here return false } return count > 0 } // ... app.Use(cors.New(cors.Config{ AllowOriginsFunc: func(origin string) bool { return dbCheckOrigin(db, origin) }, })) ``` ### Prohibited usage The following example is prohibited because it can expose your application to security risks. It sets `AllowOrigins` to `"*"` (a wildcard) and `AllowCredentials` to `true`. ```go app.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowCredentials: true, })) ``` This will result in the following panic: ```text panic: [CORS] Configuration error: When 'AllowCredentials' is set to true, 'AllowOrigins' cannot contain a wildcard origin '*'. Please specify allowed origins explicitly or adjust 'AllowCredentials' setting. ``` ## Config | Property | Type | Description | Default | |:---------------------|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------| | AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note: If true, AllowOrigins cannot be set to a wildcard (`"*"`) to prevent security vulnerabilities. | `false` | | AllowHeaders | `[]string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `[]` | | AllowMethods | `[]string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET, POST, HEAD, PUT, DELETE, PATCH, QUERY"` | | AllowOrigins | `[]string` | AllowOrigins defines a list of origins that may access the resource. This supports subdomain matching, so you can use a value like "https://*.example.com" to allow any subdomain of example.com to submit requests. If the special wildcard `"*"` is present in the list, all origins will be allowed. | `["*"]` | | AllowOriginsFunc | `func(origin string) bool` | `AllowOriginsFunc` is a function that dynamically determines whether to allow a request based on its origin. If this function returns `true`, the 'Access-Control-Allow-Origin' response header will be set to the request's 'origin' header. This function is only used if the request's origin doesn't match any origin in `AllowOrigins`. | `nil` | | AllowPrivateNetwork | `bool` | Indicates whether the `Access-Control-Allow-Private-Network` response header should be set to `true`, allowing requests from private networks. This aligns with modern security practices for web applications interacting with private networks. | `false` | | DisableValueRedaction | `bool` | Disables redaction of misconfigured origins and settings in panics and logs. | `false` | | ExposeHeaders | `[]string` | ExposeHeaders defines an allowlist of headers that clients are allowed to access. | `[]` | | MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, the Access-Control-Max-Age header will not be added and the browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header to 0. | `0` | | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | :::note If AllowOrigins is a zero value `[]string{}`, and AllowOriginsFunc is provided, the middleware will not default to allowing all origins with the wildcard value "*". Instead, it will rely on the AllowOriginsFunc to dynamically determine whether to allow a request based on its origin. This provides more flexibility and control over which origins are allowed. ::: ## Default Config ```go var ConfigDefault = Config{ Next: nil, AllowOriginsFunc: nil, AllowOrigins: []string{"*"}, DisableValueRedaction: false, AllowMethods: []string{ fiber.MethodGet, fiber.MethodPost, fiber.MethodHead, fiber.MethodPut, fiber.MethodDelete, fiber.MethodPatch, fiber.MethodQuery, }, AllowHeaders: []string{}, AllowCredentials: false, ExposeHeaders: []string{}, MaxAge: 0, AllowPrivateNetwork: false, } ``` ## Subdomain Matching The `AllowOrigins` configuration supports matching subdomains at any level. This means you can use a value like `"https://*.example.com"` to allow any subdomain of `example.com` to submit requests, including multiple subdomain levels such as `"https://sub.sub.example.com"`. ### Example If you want to allow CORS requests from any subdomain of `example.com`, including nested subdomains, you can configure the `AllowOrigins` like so: ```go app.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://*.example.com"}, })) ``` ## How It Works The CORS middleware works by adding the necessary CORS headers to responses from your Fiber application. These headers tell browsers what origins, methods, and headers are allowed for cross-origin requests. When a request arrives, the middleware first checks whether it is a preflight request—a CORS mechanism that determines if the actual request is safe to send. Preflight requests are HTTP OPTIONS requests with specific CORS headers. If the request is preflight, the middleware responds with the appropriate CORS headers and ends the request. :::note Preflight requests are typically sent by browsers before making actual cross-origin requests, especially for methods other than GET or POST, or when custom headers are used. A preflight request is an HTTP OPTIONS request that includes the `Origin`, `Access-Control-Request-Method`, and optionally `Access-Control-Request-Headers` headers. The browser sends this request to check if the server allows the actual request method and headers. ::: If the request is not preflight, the middleware adds the CORS headers to the response and passes the request to the next handler. The actual CORS headers added depend on the configuration of the middleware. The `AllowOrigins` option controls which origins can make cross-origin requests. The middleware handles different `AllowOrigins` configurations as follows: - **Single origin:** If `AllowOrigins` is set to a single origin like `"http://www.example.com"`, and that origin matches the origin of the incoming request, the middleware adds the header `Access-Control-Allow-Origin: http://www.example.com` to the response. - **Multiple origins:** If `AllowOrigins` is set to multiple origins like `"https://example.com, https://www.example.com"`, the middleware picks the origin that matches the origin of the incoming request. - **Subdomain matching:** If `AllowOrigins` includes `"https://*.example.com"`, a subdomain like `https://sub.example.com` will be matched and `"https://sub.example.com"` will be the header. This will also match `https://sub.sub.example.com` and so on, but not `https://example.com`. - **Wildcard origin:** If `AllowOrigins` is set to `"*"`, the middleware uses that and adds the header `Access-Control-Allow-Origin: *` to the response. In all cases above, except the **Wildcard origin**, the middleware will either add the `Access-Control-Allow-Origin` header to the response matching the origin of the incoming request, or it will not add the header at all if the origin is not allowed. - **Programmatic origin validation:**: The middleware also handles the `AllowOriginsFunc` option, which allows you to programmatically determine if an origin is allowed. If `AllowOriginsFunc` returns `true` for an origin, the middleware sets the `Access-Control-Allow-Origin` header to that origin. - **Null origin handling:** The middleware accepts the special literal value `"null"` as a valid origin. According to the [CORS specification](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS#origin), browsers send `"null"` as the origin for certain privacy-sensitive contexts, such as: - Requests from sandboxed iframes - Requests from `file://` URLs - Requests from `data:` URLs - Cross-origin redirects When using `AllowOriginsFunc`, if the function returns `true` for the literal string `"null"`, the middleware will set `Access-Control-Allow-Origin: null` in the response. The `"null"` origin is case-sensitive and must be lowercase. The `AllowMethods` option controls which HTTP methods are allowed. For example, if `AllowMethods` is set to `"GET, POST"`, the middleware adds the header `Access-Control-Allow-Methods: GET, POST` to the response. The `AllowHeaders` option specifies which headers are allowed in the actual request. The middleware sets the Access-Control-Allow-Headers response header to the value of `AllowHeaders`. This informs the client which headers it can use in the actual request. The `AllowCredentials` option indicates whether the response to the request can be exposed when the credentials flag is true. If `AllowCredentials` is set to `true`, the middleware adds the header `Access-Control-Allow-Credentials: true` to the response. To prevent security vulnerabilities, `AllowCredentials` cannot be set to `true` if `AllowOrigins` is set to a wildcard (`*`). The `ExposeHeaders` option defines an allowlist of headers that clients are allowed to access. If `ExposeHeaders` is set to `"X-Custom-Header"`, the middleware adds the header `Access-Control-Expose-Headers: X-Custom-Header` to the response. The `MaxAge` option indicates how long the results of a preflight request can be cached. If `MaxAge` is set to `3600`, the middleware adds the header `Access-Control-Max-Age: 3600` to the response. The `Vary` header helps caches store the correct response. For simple requests the middleware sets `Vary: Origin` unless all origins are allowed. Preflight responses add `Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers` (and `Access-Control-Request-Private-Network` when enabled and requested). This ensures caches know when to reuse a response and when to revalidate with the server. ## Infrastructure Considerations When deploying Fiber applications behind infrastructure components like CDNs, API gateways, load balancers, or reverse proxies, you have two main options for handling CORS: ### Option 1: Use Infrastructure-Level CORS (Recommended) **For most production deployments, it is often preferable to handle CORS at the infrastructure level** rather than in your Fiber application. This approach offers several advantages: - **Better Performance**: CORS headers are added at the edge, closer to the client - **Reduced Server Load**: Preflight requests are handled without reaching your application - **Centralized Configuration**: Manage CORS policies alongside other infrastructure settings - **Built-in Caching**: Infrastructure providers optimize CORS response caching **Common infrastructure CORS solutions:** - **CDNs**: CloudFront, CloudFlare, Azure CDN - handle CORS at edge locations - **API Gateways**: AWS API Gateway, Google Cloud API Gateway - centralized CORS management - **Load Balancers**: Application Load Balancers with CORS rules - **Reverse Proxies**: Nginx, Apache with CORS modules If using infrastructure-level CORS, **disable Fiber's CORS middleware** to avoid conflicts: ```go // Don't use both - choose one approach // app.Use(cors.New()) // Remove this line when using infrastructure CORS ``` ### Option 2: Application-Level CORS (Fiber Middleware) Use Fiber's CORS middleware when you need: - **Dynamic origin validation** based on application logic - **Fine-grained control** over CORS policies per route - **Integration with application state** (database-driven origins, etc.) - **Development environments** where infrastructure CORS isn't available If choosing this approach, ensure that **all CORS headers reach your Fiber application unchanged**. ### Required Headers for CORS Preflight Requests For CORS preflight requests to work correctly, these headers **must not be stripped or modified by caching layers**: - `Origin` - Required to identify the requesting origin - `Access-Control-Request-Method` - Required to identify the HTTP method for the actual request - `Access-Control-Request-Headers` - Optional, contains custom headers the actual request will use - `Access-Control-Request-Private-Network` - Optional, for private network access requests :::warning Critical Preflight Requirement If the `Access-Control-Request-Method` header is missing from an OPTIONS request, Fiber will not recognize them as CORS preflight requests. Instead, they'll be treated as regular OPTIONS requests, which typically return `405 Method Not Allowed` since most applications don't define explicit OPTIONS handlers. ::: ### CORS Response Headers (Set by Fiber) The middleware sets these response headers based on your configuration: **For all CORS requests:** - `Access-Control-Allow-Origin` - Set to the allowed origin or "*" - `Access-Control-Allow-Credentials` - Set to "true" when `AllowCredentials: true` - `Access-Control-Expose-Headers` - Lists headers the client can access - `Vary` - Set to "Origin" (unless wildcard origins are used) **For preflight responses only:** - `Access-Control-Allow-Methods` - Lists allowed HTTP methods - `Access-Control-Allow-Headers` - Lists allowed request headers (or echoes the request) - `Access-Control-Max-Age` - Cache duration for preflight results (if MaxAge > 0) - `Access-Control-Allow-Private-Network` - Set to "true" when private network access is allowed - `Vary` - Set to "Access-Control-Request-Method, Access-Control-Request-Headers, Origin" ### Common Infrastructure Issues **CDNs (CloudFront, CloudFlare, etc.)**: - Configure cache policies to forward all CORS headers - Ensure OPTIONS requests are not cached inappropriately or cache them correctly with proper Vary headers - Don't strip or modify CORS request headers **API Gateways**: - Choose either gateway-level CORS OR application-level CORS, not both - If using gateway CORS, disable Fiber's CORS middleware - If forwarding to Fiber, ensure all headers pass through unchanged **Load Balancers/Reverse Proxies**: - Preserve all HTTP headers, especially CORS-related ones - Don't modify or strip `Origin`, `Access-Control-Request-*` headers **WAFs/Security Services**: - Whitelist CORS headers in security rules - Ensure OPTIONS requests with CORS headers aren't blocked ### Debugging CORS Issues Add this middleware **before** your CORS configuration to debug what headers Fiber receives: ```go // Debug middleware to log CORS preflight requests // Only use in development or testing environments app.Use(func(c *fiber.Ctx) error { if c.Method() == "OPTIONS" { fmt.Printf("OPTIONS %s\n", c.Path()) fmt.Printf(" Origin: %s\n", c.Get("Origin")) fmt.Printf(" Access-Control-Request-Method: %s\n", c.Get("Access-Control-Request-Method")) fmt.Printf(" Access-Control-Request-Headers: %s\n", c.Get("Access-Control-Request-Headers")) } return c.Next() }) app.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://yourdomain.com"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, })) ``` Test CORS preflight directly with curl: ```bash # Test preflight request curl -X OPTIONS https://your-app.com/api/test \ -H "Origin: https://yourdomain.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type" \ -v # Test simple CORS request curl -X GET https://your-app.com/api/test \ -H "Origin: https://yourdomain.com" \ -v ``` ### Caching Considerations The middleware sets appropriate `Vary` headers to ensure proper caching: - **Non-wildcard origins**: `Vary: Origin` is set to cache responses per origin - **Preflight requests**: `Vary: Access-Control-Request-Method, Access-Control-Request-Headers, Origin` - **OPTIONS without preflight headers**: `Vary: Origin` to avoid cache poisoning Ensure your infrastructure respects these `Vary` headers for correct caching behavior. ### Choosing the Right Approach | Scenario | Recommended Approach | |----------|---------------------| | Production with CDN/API Gateway | Infrastructure-level CORS | | Dynamic origin validation needed | Application-level CORS | | Microservices with different CORS policies | Application-level CORS | | Simple static origins | Infrastructure-level CORS | | Development/testing | Application-level CORS | | High traffic applications | Infrastructure-level CORS | :::tip Infrastructure CORS Configuration Most cloud providers offer comprehensive CORS documentation: - [AWS CloudFront CORS](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors) - [Google Cloud CORS](https://cloud.google.com/storage/docs/cross-origin) - [Azure CDN CORS](https://docs.microsoft.com/en-us/azure/cdn/cdn-cors) - [CloudFlare CORS](https://developers.cloudflare.com/fundamentals/get-started/reference/http-request-headers/#cf-connecting-ip) Configure CORS at the infrastructure level when possible for optimal performance and reduced complexity. ::: ## Security Considerations When configuring CORS, misconfiguration can potentially expose your application to various security risks. Here are some secure configurations and common pitfalls to avoid: ### Secure Configurations - **Specify Allowed Origins**: Instead of using a wildcard (`"*"`), specify the exact domains allowed to make requests. For example, `AllowOrigins: "https://www.example.com, https://api.example.com"` ensures only these domains can make cross-origin requests to your application. - **Use Credentials Carefully**: If your application needs to support credentials in cross-origin requests, ensure `AllowCredentials` is set to `true` and specify exact origins in `AllowOrigins`. Do not use a wildcard origin in this case. - **Limit Exposed Headers**: Only allowlist headers that are necessary for the client-side application by setting `ExposeHeaders` appropriately. This minimizes the risk of exposing sensitive information. ### Common Pitfalls - **Wildcard Origin with Credentials**: Setting `AllowOrigins` to `"*"` (a wildcard) and `AllowCredentials` to `true` is a common misconfiguration. This combination is prohibited because it can expose your application to security risks. - **Overly Permissive Origins**: Specifying too many origins or using overly broad patterns (e.g., `https://*.example.com`) can inadvertently allow malicious sites to interact with your application. Be as specific as possible with allowed origins. - **Inadequate `AllowOriginsFunc` Validation**: When using `AllowOriginsFunc` for dynamic origin validation, ensure the function includes robust checks to prevent unauthorized origins from being accepted. Overly permissive validation can lead to security vulnerabilities. Never allow `AllowOriginsFunc` to return `true` for all origins. This is particularly crucial when `AllowCredentials` is set to `true`. Doing so can bypass the restriction of using a wildcard origin with credentials, exposing your application to serious security threats. If you need to allow wildcard origins, use `AllowOrigins` with a wildcard `"*"` instead of `AllowOriginsFunc`. Remember, the key to secure CORS configuration is specificity and caution. By carefully selecting which origins, methods, and headers are allowed, you can help protect your application from cross-origin attacks. --- ## CSRF The CSRF middleware protects against [Cross-Site Request Forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) attacks by validating tokens on unsafe HTTP methods such as POST, PUT, and DELETE. It responds with 403 Forbidden when validation fails. Safe methods (`GET`, `HEAD`, `OPTIONS`, `TRACE`, `QUERY`) are not validated; note that `QUERY` is classified as safe per RFC 10008, so do not perform state changes in `QUERY` handlers. ## Table of Contents - [Quick Start](#quick-start) - [Best Practices & Production Requirements](#best-practices--production-requirements) - [Configuration by Application Type](#configuration-by-application-type) - [Recipes for Common Use Cases](#recipes-for-common-use-cases) - [Using CSRF Tokens](#using-csrf-tokens) - [Security Model](#security-model) - [Token Extractors](#token-extractors) - [Advanced Configuration](#advanced-configuration) - [API Reference](#api-reference) - [Config Properties](#config-properties) - [Error Types](#error-types) - [Constants](#constants) ## Quick Start ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/fiber/v3/middleware/csrf" ) // Default config (development only) app.Use(csrf.New()) // Production config app.Use(csrf.New(csrf.Config{ CookieName: "__Host-csrf_", CookieSecure: true, CookieHTTPOnly: true, // false for SPAs CookieSameSite: "Lax", CookieSessionOnly: true, Extractor: extractors.FromHeader("X-Csrf-Token"), Session: sessionStore, // Redaction is enabled by default. Set DisableValueRedaction when you must expose tokens or storage keys in diagnostics. // DisableValueRedaction: true, })) ``` ## Best Practices & Production Requirements :::danger Production Requirements - `CookieSecure: true` (HTTPS only) - `CookieSameSite: "Lax"` or `"Strict"` - Use `Session` store for better security ::: 1. **Always use HTTPS** in production 2. **Use sessions** for authenticated applications 3. **Set `CookieSecure: true`** and appropriate SameSite values 4. **Implement XSS protection** alongside CSRF 5. **Regenerate tokens** after auth changes 6. **Use `__Host-` cookie prefix** when possible :::warning BREACH Protection To mitigate BREACH attacks, ensure your pages are served over HTTPS, disable HTTP compression, and implement rate limiting for requests. The CSRF token is sent as a header on every request, so if you include the token in a page that is vulnerable to BREACH, an attacker may be able to extract the token. ::: ## Configuration by Application Type ### Server-Side Rendered Apps ```go app.Use(csrf.New(csrf.Config{ CookieName: "__Host-csrf_", CookieSecure: true, CookieHTTPOnly: true, // Secure - blocks JavaScript CookieSameSite: "Lax", CookieSessionOnly: true, Extractor: extractors.FromForm("_csrf"), Session: sessionStore, })) ``` ### Single Page Applications (SPAs) ```go app.Use(csrf.New(csrf.Config{ CookieName: "__Host-csrf_", CookieSecure: true, CookieHTTPOnly: false, // Required for JavaScript access to tokens CookieSameSite: "Lax", CookieSessionOnly: true, Extractor: extractors.FromHeader("X-Csrf-Token"), Session: sessionStore, })) ``` :::warning SPA Security Trade-off SPAs require `CookieHTTPOnly: false` to access tokens via JavaScript. This slightly increases XSS risk but is necessary for SPA functionality. ::: ## Recipes for Common Use Cases - **Without Sessions**: [CSRF Recipe](https://github.com/gofiber/recipes/tree/master/csrf) - Simple Double Submit Cookie pattern - **With Sessions**: [CSRF with Session Recipe](https://github.com/gofiber/recipes/tree/master/csrf-with-session) - More secure Synchronizer Token pattern ## Using CSRF Tokens ### Server-Side Forms ```go func formHandler(c fiber.Ctx) error { token := csrf.TokenFromContext(c) return c.SendString(fmt.Sprintf(`
`, token)) } ``` ### Single Page Applications ```go func apiHandler(c fiber.Ctx) error { token := csrf.TokenFromContext(c) return c.JSON(fiber.Map{ "csrf_token": token, "data": "your data", }) } ``` ```javascript // Get CSRF token from cookie function getCsrfToken() { const value = `; ${document.cookie}`; const parts = value.split(`; __Host-csrf_=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // Use with fetch API async function makeRequest(url, data) { const csrfToken = getCsrfToken(); const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Csrf-Token': csrfToken }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } ``` ## Security Model The middleware employs a robust, defense-in-depth strategy to protect against CSRF attacks. The primary defense is token-based validation, which operates in one of two modes depending on your configuration. This is supplemented by a mandatory secondary check on the request's origin. ### Fetch Metadata Guardrails - **Sec-Fetch-Site**: For unsafe methods, the middleware inspects the [`Sec-Fetch-Site`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site) header when present. If the header value is not one of "same-origin", "none", "same-site", or "cross-site", the request is rejected with `ErrFetchSiteInvalid`. If the header is valid or absent, the request proceeds to the standard origin and token validation checks. This provides an early check to block requests with invalid `Sec-Fetch-Site` values, while allowing legitimate same-site and cross-site requests to be validated by the existing mechanisms. ### 1. Token Validation Patterns #### Double Submit Cookie (Default Mode) This is the default pattern, used when a `Session` store is **not** configured. It is a "semi-stateless" approach; while it doesn't tie tokens to a specific user session, the server still maintains a record of all validly issued tokens. - **How it Works:** 1. On a user's first visit (or a safe request like `GET`), the middleware generates a unique token. 2. This token is sent to the client in a `Set-Cookie` header. 3. The server also stores this token (in memory by default or in the configured `Storage`). It confirms the token is server-generated and still valid, but it is not tied to a specific user. 4. For subsequent unsafe requests (e.g., `POST`, `PUT`), the client must read the token from the cookie and echo it in a different location, such as the `X-Csrf-Token` header. - **Validation:** The middleware validates three things: that the token from the header/form **exactly matches** the token from the cookie, that the token **exists** in the server-side storage, and that it **has not expired**. - **Why it is secure:** Attackers on a malicious domain cannot read the victim's cookie to forge a matching header. They also cannot invent a token because it wouldn't exist in the server's storage registry. #### Synchronizer Token (Session-Based Mode) This is a more secure, stateful pattern that is **automatically enabled** when you provide a `Session` store in the configuration. - **How it Works:** 1. A unique token is generated and stored directly within the user's session data on the server. 2. The token is also sent to the client as a cookie. 3. For unsafe requests, the client sends the token back in a header or form field. - **Validation:** The middleware performs a multi-step validation: 1. It first performs the standard **Double Submit Cookie check**: the token from the header/form must exactly match the token from the cookie. This is a fast and efficient first line of defense, and there is little benefit of skipping it. 2. It then validates that this token exists and is valid within the user's **server-side session**. This is the authoritative check that ties the token to the authenticated user. - **Why it is more secure:** Tying the token to the server-side session provides the strongest CSRF protection, as the token is then guaranteed to have been generated for the specific user. While browsers automatically send the required cookie, custom API clients must remember to include the cookie with their requests for validation to succeed. ```go // Enable the more secure Synchronizer Token pattern app.Use(csrf.New(csrf.Config{ Session: sessionStore, // Providing a session store activates this mode })) ``` ### 2. Origin & Referer Validation As a crucial second layer of defense, the middleware **always** performs `Origin` and `Referer` header checks for unsafe requests (when the connection is HTTPS). - The request's `Origin` (for cross-origin requests) or `Referer` (for same-origin requests) header **must** match the application's `Host` header or be explicitly allowed in the `TrustedOrigins` list. - This check is performed *in addition* to token validation and provides strong protection because these headers are reliably set by browsers and cannot be programmatically controlled by an attacker from a malicious site. ## Token Extractors This middleware uses the shared `extractors` package for token extraction. For full details on extractor types, chaining, security, and advanced usage, see the [Extractors Guide](../guide/extractors). **Extractor Source Constants:** Extractor source constants (such as `SourceHeader`, `SourceForm`, etc.) are defined in the shared extractors package, not in the CSRF middleware itself. Refer to the Extractors Guide for their definitions and usage. ### CSRF-Specific Extractor Notes For CSRF protection, prefer secure extraction methods: - **Headers** (`extractors.FromHeader("X-Csrf-Token")`) – Most secure, not logged in URLs - **Form data** (`extractors.FromForm("_csrf")`) – Secure for form submissions - **Avoid URL parameters** – Query/param extractors expose tokens in logs and browser history :::note What about cookies? **Cookies are generally not a secure source for CSRF tokens.** The middleware will panic if you configure an extractor that reads from cookies with the same name as your CSRF cookie. This is because reading the CSRF token from a cookie with the same name as the CSRF cookie defeats CSRF protection entirely, as the extracted token will always match the cookie value, allowing any CSRF attack to succeed. **Advanced usage:** In rare cases, you may securely extract a CSRF token from a cookie if: - You read from a different cookie (not the CSRF cookie itself) - You use multiple cookies for custom validation - You implement custom logic across different cookie sources If you do this, set the extractor’s `Source` to `SourceCookie` and allow the middleware to check that the cookie name is different from your CSRF cookie. It will panic if this is the case. **Warning:** Cookie-based extraction is strongly discouraged, as it is easy to misconfigure and creates security risks. Prefer extracting tokens from headers or form fields for robust CSRF protection. See the [Extractors Guide](../guide/extractors#security-considerations) for more details. ::: ### Route-Specific Configuration You can configure different extraction methods for different routes: ```go // API routes - header extraction for AJAX/fetch requests api := app.Group("/api") api.Use(csrf.New(csrf.Config{ Extractor: extractors.FromHeader("X-Csrf-Token"), })) // Form routes - form field extraction for traditional forms forms := app.Group("/forms") forms.Use(csrf.New(csrf.Config{ Extractor: extractors.FromForm("_csrf"), })) ``` ### Custom CSRF Extractors For specialized CSRF token extraction needs, you can create custom extractors. See the [Extractors Guide](../guide/extractors#custom-extraction-logic) for advanced patterns and security notes. :::danger Never Extract from Cookies **NEVER create custom extractors that read from cookies using the same `CookieName` as your CSRF configuration.** This completely defeats CSRF protection by making the extracted token always match the cookie value, allowing any CSRF attack to succeed. ```go // ❌ NEVER DO THIS - Completely defeats CSRF protection badExtractor := csrf.Extractor{ Extract: func(c fiber.Ctx) (string, error) { return c.Cookies("csrf_"), nil // Always passes validation! }, Source: csrf.SourceCustom, // See extractors.SourceCustom in shared package Key: "csrf_", } // ✅ DO THIS - Extract from different source than cookie app.Use(csrf.New(csrf.Config{ CookieName: "csrf_", Extractor: extractors.FromHeader("X-Csrf-Token"), // Header vs cookie comparison })) ``` The middleware uses the **Double Submit Cookie** pattern – it compares the extracted token against the cookie value. If you configure an extractor that reads from the same cookie, it will panic because they will always match and provide zero CSRF protection. ::: #### Bearer Token Embedding & Custom Extractors You can create advanced extractors for use cases like JWT embedding or JSON body parsing. See the [Extractors Guide](../guide/extractors#custom-extraction-logic) for secure implementation patterns and more examples. ### Fallback Extraction For applications that need to support both AJAX and form submissions: ```go // Try header first (AJAX), fall back to form (traditional forms) app.Use(csrf.New(csrf.Config{ Extractor: extractors.Chain( extractors.FromHeader("X-Csrf-Token"), extractors.FromForm("_csrf"), ), })) ``` :::warning Chaining extractors increases complexity. Use only when you need to support multiple client types. See the [Extractors Guide](../guide/extractors#chain-ordering-strategy) for details and security notes. ::: ## Advanced Configuration ### Trusted Origins ```go app.Use(csrf.New(csrf.Config{ TrustedOrigins: []string{ "https://trusted.example.com", "https://*.example.com", // Wildcard subdomains }, })) ``` ### Custom Error Handler ```go app.Use(csrf.New(csrf.Config{ ErrorHandler: func(c fiber.Ctx, err error) error { accepts := c.Accepts("html", "json") path := c.Path() if accepts == "json" || strings.HasPrefix(path, "/api/") { return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ "error": "Forbidden", }) } return c.Status(fiber.StatusForbidden).Render("error", fiber.Map{ "Title": "Forbidden", "Status": fiber.StatusForbidden, }, "layouts/main") }, })) ``` ### Custom Storage/Database You can use any storage from our [storage](https://github.com/gofiber/storage/) package. ```go storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3/v2 app.Use(csrf.New(csrf.Config{ Storage: storage, })) ``` ### Token Management ```go // Delete token (e.g., on logout) handler := csrf.HandlerFromContext(c) if handler != nil { if err := handler.DeleteToken(c); err != nil { // handle error, e.g. log it } } // With session middleware // Destroying the session will also remove the CSRF token if using session-based CSRF. session.Destroy() ``` ## API Reference ```go // Create middleware func New(config ...csrf.Config) fiber.Handler // Get token from context func TokenFromContext(ctx any) string // Get handler from context func HandlerFromContext(ctx any) *csrf.Handler // Delete token func (h *csrf.Handler) DeleteToken(c fiber.Ctx) error ``` `TokenFromContext` and `HandlerFromContext` accept a `fiber.CustomCtx`, `fiber.Ctx`, a `*fasthttp.RequestCtx`, or a `context.Context`. ## Config Properties | Property | Type | Description | Default | |:------------------|:-----------------------------------|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| | Next | `func(fiber.Ctx) bool` | Skip middleware when returns true | `nil` | | CookieName | `string` | CSRF cookie name | `"csrf_"` | | CookieDomain | `string` | CSRF cookie domain | `""` | | CookiePath | `string` | CSRF cookie path | `""` | | CookieSecure | `bool` | HTTPS only cookie (**required for production**) | `false` | | CookieHTTPOnly | `bool` | Prevent JavaScript access (**use `false` for SPAs**) | `false` | | CookieSameSite | `string` | SameSite attribute (**use "Lax" or "Strict"**) | `"Lax"` | | CookieSessionOnly | `bool` | Session-only cookie (expires on browser close) | `false` | | IdleTimeout | `time.Duration` | Token expiration time | `30 * time.Minute` | | KeyGenerator | `func() string` | Token generation function | `utils.SecureToken` | | ErrorHandler | `fiber.ErrorHandler` | Custom error handler | `defaultErrorHandler` | | Extractor | `extractors.Extractor` | Token extraction method with metadata | `extractors.FromHeader("X-Csrf-Token")` | | DisableValueRedaction | `bool` | Disables redaction of tokens and storage keys in logs and error messages. | `false` | | Session | `*session.Store` | Session store (**recommended for production**) | `nil` | | Storage | `fiber.Storage` | Token storage (overridden by Session) | `nil` | | TrustedOrigins | `[]string` | Trusted origins for cross-origin requests | `[]` | | SingleUseToken | `bool` | Generate new token after each use | `false` | ## Error Types ```go var ( ErrTokenNotFound = errors.New("csrf: token not found") ErrTokenInvalid = errors.New("csrf: token invalid") ErrRefererNotFound = errors.New("csrf: referer header missing") ErrRefererInvalid = errors.New("csrf: referer header invalid") ErrRefererNoMatch = errors.New("csrf: referer does not match host or trusted origins") ErrOriginInvalid = errors.New("csrf: origin header invalid") ErrOriginNoMatch = errors.New("csrf: origin does not match host or trusted origins") ) ``` ## Constants ```go const ( HeaderName = "X-Csrf-Token" ) ``` --- ## EarlyData The Early Data middleware adds TLS 1.3 "0-RTT" support to [Fiber](https://github.com/gofiber/fiber). When the client and server share a PSK, TLS 1.3 lets the client send data with the first flight and skip the initial round trip. Enable Fiber's `TrustProxy` option before using this middleware to avoid spoofed client headers. When `TrustProxy` is disabled (the default) or the remote address is not trusted by your proxy configuration, requests carrying the `Early-Data` header are rejected with `425 Too Early` to prevent 0-RTT spoofing from direct clients. Enabling early data in a reverse proxy (for example, `ssl_early_data on;` in nginx) makes requests replayable. Review these resources before proceeding: - [datatracker](https://datatracker.ietf.org/doc/html/rfc8446#section-8) - [trailofbits](https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt) By default, the middleware permits early data only for safe methods (`GET`, `HEAD`, `OPTIONS`, `TRACE`, `QUERY`) and rejects other requests before your handler runs. Override this behavior with the `AllowEarlyData` option. ## Signatures ```go func New(config ...Config) fiber.Handler func IsEarly(c fiber.Ctx) bool ``` `IsEarly` returns `true` when a request used early data and the middleware allowed it to proceed. ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/earlydata" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(earlydata.New()) // Or extend your config for customization app.Use(earlydata.New(earlydata.Config{ Error: fiber.ErrTooEarly, // ... })) ``` ## Config | Property | Type | Description | Default | |:---------------|:------------------------|:-----------|:-------------------------------------------------------| | Next | `func(fiber.Ctx) bool` | Skip this middleware when the function returns true. | `nil` | | IsEarlyData | `func(fiber.Ctx) bool` | Reports whether the request used early data. | Function checking if "Early-Data" header equals "1" | | AllowEarlyData | `func(fiber.Ctx) bool` | Decides if an early-data request should be allowed. | Function rejecting on unsafe and allowing safe methods | | Error | `error` | Returned when an early-data request is rejected. | `fiber.ErrTooEarly` | ## Default Config ```go var ConfigDefault = Config{ IsEarlyData: func(c fiber.Ctx) bool { return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue }, AllowEarlyData: func(c fiber.Ctx) bool { return fiber.IsMethodSafe(c.Method()) }, Error: fiber.ErrTooEarly, } ``` ## Constants ```go const ( DefaultHeaderName = "Early-Data" DefaultHeaderTrueValue = "1" ) ``` --- ## Encrypt Cookie The Encrypt Cookie middleware for [Fiber](https://github.com/gofiber/fiber) encrypts cookie values for secure storage. :::note This middleware encrypts cookie values but not cookie names. ::: ## Signatures ```go // Initializes the middleware func New(config ...Config) fiber.Handler // GenerateKey returns a random string of 16, 24, or 32 bytes. // The length of the key determines the AES encryption algorithm used: // 16 bytes for AES-128, 24 bytes for AES-192, and 32 bytes for AES-256-GCM. func GenerateKey(length int) string ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/encryptcookie" ) ``` Once your Fiber app is initialized, register the middleware: ```go // Provide a minimal configuration app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-32-character-string", })) // Retrieve the encrypted cookie value app.Get("/", func(c fiber.Ctx) error { return c.SendString("value=" + c.Cookies("test")) }) // Create an encrypted cookie app.Post("/", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "test", Value: "SomeThing", }) return nil }) ``` :::note Use an encoded key of 16, 24, or 32 bytes to select AES‑128, AES‑192, or AES‑256‑GCM. Generate a stable key with `openssl rand -base64 32` or `encryptcookie.GenerateKey(32)` and store it securely. Generating a new key on each startup renders existing cookies unreadable. ::: ## Config | Property | Type | Description | Default | |:----------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------|:-----------------------------| | Next | `func(fiber.Ctx) bool` | A function to skip this middleware when it returns true. | `nil` | | Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | | Key | `string` | A base64-encoded unique key to encode & decode cookies. Required. Key length should be 16, 24, or 32 bytes. | (No default, required field) | | Encryptor | `func(name, decryptedString, key string) (string, error)` | A custom function to encrypt cookies. | `EncryptCookie` | | Decryptor | `func(name, encryptedString, key string) (string, error)` | A custom function to decrypt cookies. | `DecryptCookie` | ### Encryptor and Decryptor parameters Custom encryptor and decryptor functions receive three arguments: - `name`: The cookie name. The default helpers bind this value as additional authenticated data (AAD) so encrypted values can only be decrypted for the same cookie. - `string`: The cookie payload. `EncryptCookie` accepts the decrypted value and returns ciphertext, while `DecryptCookie` receives ciphertext and must return the decrypted value. - `key`: The base64-encoded key pulled from the middleware configuration. Use it to derive or validate any encryption keys your implementation requires. ## Default Config ```go var ConfigDefault = Config{ Next: nil, Except: []string{}, Key: "", Encryptor: EncryptCookie, Decryptor: DecryptCookie, } ``` ## Use with Other Middleware That Reads or Modifies Cookies Place `encryptcookie` before middleware that reads or writes cookies. If you use the CSRF middleware, register `encryptcookie` first so it can read the token. Exclude cookies from encryption by listing them in `Except`. If a frontend framework such as Angular reads the CSRF token from a cookie, add that name to the `Except` array: ```go app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", Except: []string{csrf.ConfigDefault.CookieName}, // exclude CSRF cookie })) app.Use(csrf.New(csrf.Config{ Extractor: csrf.FromHeader(csrf.HeaderName), CookieSameSite: "Lax", CookieSecure: true, CookieHTTPOnly: false, })) ``` ## Encryption Algorithms The default Encryptor and Decryptor functions use `AES-256-GCM` for encryption and decryption. If you need to use `AES-128` or `AES-192` instead, you can do so by changing the length of the key when calling `encryptcookie.GenerateKey(length)` or by providing a key of one of the following lengths: - AES-128 requires a 16-byte key. - AES-192 requires a 24-byte key. - AES-256 requires a 32-byte key. For example, to generate a key for AES-128: ```go key := encryptcookie.GenerateKey(16) ``` And for AES-192: ```go key := encryptcookie.GenerateKey(24) ``` --- ## EnvVar EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) exposes environment variables with configurable options. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/envvar" ) ``` Once your Fiber app is initialized, configure the middleware as shown: ```go // Initialize default config (exports no variables) app.Use("/expose/envvars", envvar.New()) // Or extend your config for customization app.Use("/expose/envvars", envvar.New( envvar.Config{ ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, }), ) ``` :::note Mount the middleware on a path; it cannot be used without one. ::: ## Response Sample response: ```json { "vars": { "someEnvVariable": "someValue", "anotherEnvVariable": "anotherValue" } } ``` ## Config | Property | Type | Description | Default | |:------------|:--------------------|:-----------------------------------------------------------------------------|:--------| | ExportVars | `map[string]string` | ExportVars lists the environment variables to expose. | `nil` | ## Default Config ```go Config{} // Exports no environment variables ``` --- ## ETag ETag middleware for [Fiber](https://github.com/gofiber/fiber) that helps caches validate responses and saves bandwidth by avoiding full retransmits when content is unchanged. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/etag" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(etag.New()) // GET / -> ETag: "13-1831710635" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) // Or extend your config for customization app.Use(etag.New(etag.Config{ Weak: true, })) // GET / -> ETag: W/"13-1831710635" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ``` Entity tags in requests must be quoted per RFC 9110. For example: ```text If-None-Match: "example-etag" ``` ## Config | Property | Type | Description | Default | |:---------|:------------------------|:-------------------------------------------------------------------------------------------------------------------|:--------| | Weak | `bool` | Enables weak validators. Weak ETags are easier to generate but less reliable for comparisons. | `false` | | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Weak: false, } ``` --- ## ExpVar The ExpVar middleware exposes runtime variables over HTTP in JSON. Using it (e.g., `app.Use(expvarmw.New())`) registers handlers on `/debug/vars`. ## Signatures ```go func New() fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" expvarmw "github.com/gofiber/fiber/v3/middleware/expvar" ) ``` Once your Fiber app is initialized, use the middleware as shown: ```go var count = expvar.NewInt("count") app.Use(expvarmw.New()) app.Get("/", func(c fiber.Ctx) error { count.Add(1) return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) }) ``` Visit `/debug/vars` to see all variables, and append `?r=key` to filter the output. ```bash curl 127.0.0.1:3000 hello expvar count 1 curl 127.0.0.1:3000/debug/vars { "cmdline": ["xxx"], "count": 1, "expvarHandlerCalls": 33, "expvarRegexpErrors": 0, "memstats": {...} } curl 127.0.0.1:3000/debug/vars?r=c { "cmdline": ["xxx"], "count": 1 } ``` ## Config | Property | Type | Description | Default | |:---------|:------------------------|:--------------------------------------------------------------------|:--------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, } ``` --- ## Favicon Favicon middleware for [Fiber](https://github.com/gofiber/fiber) that drops repeated `/favicon.ico` requests or serves a cached icon from memory. Mount it before your logger to suppress noisy requests and avoid disk reads. It handles only `GET`, `HEAD`, and `OPTIONS` to the configured URL; other methods return `405 Method Not Allowed`. :::note This middleware only serves the default `/favicon.ico` (or a [custom URL](#config)). For multiple icons, use the Static middleware. ::: ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/favicon" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(favicon.New()) // Or extend your config for customization app.Use(favicon.New(favicon.Config{ File: "./favicon.ico", URL: "/favicon.ico", })) ``` ## Config | Property | Type | Description | Default | |:-------------|:------------------------|:---------------------------------------------------------------------------------|:---------------------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Data | `[]byte` | Raw data of the favicon file. This can be used instead of `File`. | `nil` | | File | `string` | File holds the path to an actual favicon that will be cached. | "" | | URL | `string` | URL for favicon handler. | "/favicon.ico" | | FileSystem | `fs.FS` | FileSystem is an optional alternate filesystem from which to load the favicon file (e.g. using `os.DirFS` or an `embed.FS`). | `nil` | | CacheControl | `string` | CacheControl defines how the Cache-Control header in the response should be set. | "public, max-age=31536000" | | MaxBytes | `int64` | MaxBytes limits the maximum size of the cached favicon asset. | `1048576` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, File: "", URL: fPath, CacheControl: "public, max-age=31536000", MaxBytes: 1024 * 1024, } ``` --- ## Health Check Middleware that adds liveness, readiness, and startup probes to [Fiber](https://github.com/gofiber/fiber) apps. It provides a generic handler you can mount on any route, with constants for the conventional `/livez`, `/readyz`, and `/startupz` endpoints. ## Overview Register the middleware on any endpoint you want to expose a probe on. The package exports constants for the conventional liveness, readiness, and startup endpoints: ```go app.Get(healthcheck.LivenessEndpoint, healthcheck.New()) app.Get(healthcheck.ReadinessEndpoint, healthcheck.New()) app.Get(healthcheck.StartupEndpoint, healthcheck.New()) ``` By default the probe returns `true`, so each endpoint responds with `200 OK`; returning `false` yields `503 Service Unavailable`. The default response format is plain text, but you can configure the middleware to return responses in JSON, XML, MessagePack, or CBOR formats. - **Liveness**: Checks if the server is running. - **Readiness**: Checks if the application is ready to handle requests. - **Startup**: Checks if the application has completed its startup sequence. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/healthcheck" ) ``` After your app is initialized, register the middleware on the endpoints you want to expose: ```go // Use the default probe on the conventional endpoints app.Get(healthcheck.LivenessEndpoint, healthcheck.New()) app.Get(healthcheck.ReadinessEndpoint, healthcheck.New(healthcheck.Config{ Probe: func(c fiber.Ctx) bool { return serviceA.Ready() && serviceB.Ready() }, })) app.Get(healthcheck.StartupEndpoint, healthcheck.New()) // Register a custom endpoint app.Get("/healthz", healthcheck.New()) ``` The middleware responds to GET and HEAD, where HEAD returns the same status code without a body. Use `app.All` to expose a probe on every method; other methods fall through to the next handler: ```go app.All("/healthz", healthcheck.New()) ``` ### Response Formats You can configure the response format using the `ResponseFormat` field in the config: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/healthcheck" ) // JSON format app.Get(healthcheck.LivenessEndpoint, healthcheck.New(healthcheck.Config{ ResponseFormat: healthcheck.FormatJSON, })) // Response: {"status":"OK"} // XML format app.Get(healthcheck.ReadinessEndpoint, healthcheck.New(healthcheck.Config{ ResponseFormat: healthcheck.FormatXML, })) // Response: OK ``` **Note:** MessagePack and CBOR formats require configuring the appropriate encoders in your Fiber app: ```go import ( "github.com/fxamacker/cbor/v2" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/healthcheck" "github.com/shamaton/msgpack/v3" ) app := fiber.New(fiber.Config{ MsgPackEncoder: msgpack.Marshal, CBOREncoder: cbor.Marshal, }) app.Get(healthcheck.LivenessEndpoint, healthcheck.New(healthcheck.Config{ ResponseFormat: healthcheck.FormatMsgPack, })) ``` ## Config ```go type Config struct { // Next defines a function to skip this middleware when it returns true. If this function returns true // and no other handlers are defined for the route, Fiber will return a status 404 Not Found, since // no other handlers were defined to return a different status. // // Optional. Default: nil Next func(fiber.Ctx) bool // Probe is executed to determine the current health state. It can be used for // liveness, readiness or startup checks. Returning true indicates the application // is healthy. // // Optional. Default: func(c fiber.Ctx) bool { return true } Probe func(fiber.Ctx) bool // ResponseFormat specifies the format of the healthcheck response. // Supported formats: Text (default), JSON, XML, MsgPack, CBOR. // // Optional. Default: FormatText ResponseFormat ResponseFormat } ``` ### Response Format Constants ```go type ResponseFormat int const ( FormatText ResponseFormat = iota // Plain text response (default) FormatJSON // JSON response FormatXML // XML response FormatMsgPack // MessagePack response FormatCBOR // CBOR response ) ``` ## Default Config The default configuration used by this middleware is defined as follows: ```go func defaultProbe(_ fiber.Ctx) bool { return true } var ConfigDefault = Config{ Next: nil, Probe: defaultProbe, } ``` --- ## Helmet Helmet secures your app by adding common security headers. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Once your Fiber app is initialized, add the middleware: ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/helmet" ) func main() { app := fiber.New() app.Use(helmet.New()) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome!") }) app.Listen(":3000") } ``` ## Test ```bash curl -I http://localhost:3000 ``` ## Config | Property | Type | Description | Default | |:--------------------------|:------------------------|:--------------------------------------------|:-----------------| | Next | `func(fiber.Ctx) bool` | Skips the middleware when the function returns `true`. | `nil` | | XSSProtection | `string` | Value for the `X-XSS-Protection` header. | "0" | | ContentTypeNosniff | `string` | Value for the `X-Content-Type-Options` header. | "nosniff" | | XFrameOptions | `string` | Value for the `X-Frame-Options` header. | "SAMEORIGIN" | | HSTSMaxAge | `int` | `max-age` value for `Strict-Transport-Security`. | 0 | | HSTSExcludeSubdomains | `bool` | Disables HSTS on subdomains when `true`. | false | | ContentSecurityPolicy | `string` | Value for the `Content-Security-Policy` header. | "" | | CSPReportOnly | `bool` | Enables report-only mode for CSP. | false | | HSTSPreloadEnabled | `bool` | Adds the `preload` directive to HSTS. | false | | ReferrerPolicy | `string` | Value for the `Referrer-Policy` header. | "no-referrer" | | PermissionPolicy | `string` | Value for the `Permissions-Policy` header. | "" | | CrossOriginEmbedderPolicy | `string` | Value for the `Cross-Origin-Embedder-Policy` header. | "require-corp" | | CrossOriginOpenerPolicy | `string` | Value for the `Cross-Origin-Opener-Policy` header. | "same-origin" | | CrossOriginResourcePolicy | `string` | Value for the `Cross-Origin-Resource-Policy` header. | "same-origin" | | OriginAgentCluster | `string` | Value for the `Origin-Agent-Cluster` header. | "?1" | | XDNSPrefetchControl | `string` | Value for the `X-DNS-Prefetch-Control` header. | "off" | | XDownloadOptions | `string` | Value for the `X-Download-Options` header. | "noopen" | | XPermittedCrossDomain | `string` | Value for the `X-Permitted-Cross-Domain-Policies` header. | "none" | ## Default Config ```go var ConfigDefault = Config{ XSSProtection: "0", ContentTypeNosniff: "nosniff", XFrameOptions: "SAMEORIGIN", ReferrerPolicy: "no-referrer", CrossOriginEmbedderPolicy: "require-corp", CrossOriginOpenerPolicy: "same-origin", CrossOriginResourcePolicy: "same-origin", OriginAgentCluster: "?1", XDNSPrefetchControl: "off", XDownloadOptions: "noopen", XPermittedCrossDomain: "none", } ``` --- ## Host Authorization Host authorization middleware for [Fiber](https://github.com/gofiber/fiber) that validates the incoming `Host` header against a configurable allowlist. Protects against [DNS rebinding attacks](https://en.wikipedia.org/wiki/DNS_rebinding) where an attacker-controlled domain resolves to the application's internal IP, causing browsers to send requests with a malicious Host header. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/hostauthorization" ) ``` Once your Fiber app is initialized, choose one of the following approaches: ### Basic Usage ```go app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"api.myapp.com"}, })) app.Get("/users", func(c fiber.Ctx) error { return c.JSON(getUsers()) }) // Host: api.myapp.com → 200 OK // Host: evil.com → 403 Forbidden ``` ### Subdomain Wildcards A `*.` prefix matches any subdomain but **not** the bare domain itself: ```go app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"*.myapp.com"}, })) // Host: api.myapp.com → 200 OK // Host: www.myapp.com → 200 OK // Host: myapp.com → 403 Forbidden ``` To allow both the bare domain and all subdomains, include both: ```go AllowedHosts: []string{"myapp.com", "*.myapp.com"}, ``` ### Internationalized Domain Names (IDN) Browsers always transmit the `Host` header in ASCII (Punycode) form, so IDN entries in `AllowedHosts` are converted to Punycode at startup. You can configure entries in either form — they are equivalent: ```go AllowedHosts: []string{"münchen.example.com"} // Unicode AllowedHosts: []string{"xn--mnchen-3ya.example.com"} // Punycode (what the browser sends) ``` Both match an incoming request whose Host header is `xn--mnchen-3ya.example.com`. ### Skipping Health Checks Use `Next` to bypass host validation for specific paths: ```go app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"myapp.com", "*.myapp.com"}, Next: func(c fiber.Ctx) bool { return c.Path() == "/healthz" }, })) // Host: evil.com GET /healthz → 200 OK (skipped) // Host: evil.com GET /users → 403 Forbidden ``` ### Dynamic Validation Use `AllowedHostsFunc` for hosts that can't be known at startup: ```go app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHostsFunc: func(host string) bool { // Look up tenant domains from database, cache, etc. return isRegisteredTenant(host) }, })) ``` `AllowedHostsFunc` is only called when static `AllowedHosts` don't match, so you can combine both: ```go app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"myapp.com", "*.myapp.com"}, AllowedHostsFunc: func(host string) bool { return isRegisteredCustomDomain(host) }, })) ``` ### Custom Error Response The default response is **403 Forbidden**. **421 Misdirected Request** ([RFC 9110 §15.5.20](https://www.rfc-editor.org/rfc/rfc9110#section-15.5.20)) is a semantically closer choice for "wrong host for this server" — CDNs like Cloudflare and Fastly use it for this case. Either is reasonable; pick one via `ErrorHandler`: ```go // 403 with a JSON body app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"myapp.com"}, ErrorHandler: func(c fiber.Ctx, err error) error { return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ "error": "unauthorized host", }) }, })) // 421 Misdirected Request — closer to the RFC-defined semantics app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"myapp.com"}, ErrorHandler: func(c fiber.Ctx, _ error) error { return c.SendStatus(fiber.StatusMisdirectedRequest) // 421 }, })) ``` ### Combined with Domain() Router `hostauthorization` acts as a security gate; [`Domain()`](https://docs.gofiber.io/api/app#domain) handles routing: ```go // Security layer — reject anything not from our hosts app.Use(hostauthorization.New(hostauthorization.Config{ AllowedHosts: []string{"myapp.com", "*.myapp.com"}, Next: func(c fiber.Ctx) bool { return c.Path() == "/healthz" }, })) // Routing layer — direct allowed hosts to the right handlers app.Domain("api.myapp.com").Get("/users", listUsers) app.Domain(":tenant.myapp.com").Get("/dashboard", tenantDashboard) app.Get("/healthz", healthCheck) ``` ## Config | Property | Type | Description | Default | |:-----------------|:------------------------------|:--------------------------------------------------------------------------------------------------|:--------| | Next | `func(fiber.Ctx) bool` | Defines a function to skip this middleware when returned true. | `nil` | | AllowedHosts | `[]string` | List of permitted hosts. Supports exact match and subdomain wildcard (`*.example.com`). | `nil` | | AllowedHostsFunc | `func(string) bool` | Dynamic validator called only when no static AllowedHosts rule matches. Receives the normalized hostname: port stripped, trailing dot removed, IPv6 brackets removed, lowercased, IDN converted to Punycode. | `nil` | | ErrorHandler | `fiber.ErrorHandler` | Called when a request is rejected. Receives `ErrForbiddenHost` as the error. | 403 | Either `AllowedHosts` or `AllowedHostsFunc` (or both) must be provided. The middleware panics at startup if neither is set. ## Default Config ```go var ConfigDefault = Config{} ``` There is no useful default — you must provide at least `AllowedHosts` or `AllowedHostsFunc`. ## Host Matching The middleware matches hosts in this order: 1. **Exact match** — case-insensitive, port and trailing dot stripped, IDN labels in Punycode form 2. **Subdomain wildcard** — `"*.myapp.com"` matches `api.myapp.com` but not `myapp.com` 3. **AllowedHostsFunc** — called only if no static rule matched The first match wins. If nothing matches, `ErrorHandler` is called. ## Host Normalization Before matching, both incoming hosts and `AllowedHosts` entries are normalized at startup: - Port is stripped (`example.com:8080` → `example.com`) - Trailing dot removed (`example.com.` → `example.com`) - IPv6 brackets removed (`[::1]` → `::1`) - Lowercased - IDN labels converted to ASCII/Punycode (`münchen.example.com` → `xn--mnchen-3ya.example.com`) - RFC 1035 length limits enforced at startup: ≤253 chars total, ≤63 chars per label (panic on violation) ## Filtering by Client IP This middleware filters by the `Host` *header*, not by the client's source IP. To restrict access by client IP, use Fiber's [`TrustProxy` / `TrustProxyConfig`](https://docs.gofiber.io/whats_new#trusted-proxies) configuration — those are the correct knobs for IP allowlisting and CIDR ranges of trusted proxies. ## Proxy Support The middleware uses Fiber's `c.Hostname()`, which respects `X-Forwarded-Host` when [`TrustProxy`](https://docs.gofiber.io/api/fiber#config) is enabled. When `TrustProxy` is disabled (the default), `X-Forwarded-Host` is ignored and the raw `Host` header is used. fasthttp itself is HTTP/1.x only. HTTP/2 support requires an external library (e.g. `fasthttp2`) plugged in via `Server.NextProto`. Those libraries are responsible for mapping the HTTP/2 `:authority` pseudo-header to a Host value before the request reaches Fiber handlers, so the middleware should work transparently once H2 is wired up — but this is the H2 library's responsibility, not fasthttp's or this middleware's. ## RFC Compliance - **RFC 9110 Section 7.2** — Host and port are separate components; port is stripped before matching - **RFC 9110 Section 17.1** — Origin servers should reject misdirected requests - **RFC 9112 Section 3.2** — Requests with missing Host headers should be rejected - **RFC 1035** — `AllowedHosts` entries are validated against the 253-char total / 63-char per-label limits - Returns **403 Forbidden** (not 400) because the request is syntactically valid but semantically unauthorized :::note **RFC 9110 §15.5.20** defines **421 Misdirected Request** as a semantically closer response for host mismatches ("the request was directed at a server unable or unwilling to produce an authoritative response for the target URI"). CDNs like Cloudflare and Fastly use 421 for this case. To use 421 instead of 403, set a custom `ErrorHandler`: ```go ErrorHandler: func(c fiber.Ctx, err error) error { return c.SendStatus(fiber.StatusMisdirectedRequest) // 421 }, ``` ::: --- ## Idempotency The Idempotency middleware helps build fault-tolerant APIs. Duplicate requests—such as retries after network issues—won't trigger the same action twice on the server. Refer to [IETF RFC 7231 §4.2.2](https://tools.ietf.org/html/rfc7231#section-4.2.2) for definitions of safe and idempotent HTTP methods. ## HTTP Method Categories * **Safe Methods** (do not modify server state): `GET`, `HEAD`, `OPTIONS`, `TRACE`, `QUERY` * **Idempotent Methods** (identical requests have the same effect as a single one): all safe methods **plus** `PUT` and `DELETE` > According to the RFC, safe methods never change server state, while idempotent methods may change state but remain safe to repeat. ## Signatures ```go func New(config ...Config) fiber.Handler func IsFromCache(c fiber.Ctx) bool func WasPutToCache(c fiber.Ctx) bool ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/idempotency" ) ``` Once your Fiber app is initialized, configure the middleware: ### Default Config (Skip **Safe** Methods) By default, the `Next` function skips middleware for safe methods only: ```go app.Use(idempotency.New()) ``` ### Skip **Idempotent** Methods Instead Skip all idempotent methods (including `PUT` and `DELETE`) by overriding `Next`: ```go app.Use(idempotency.New(idempotency.Config{ Next: func(c fiber.Ctx) bool { // Skip middleware for idempotent methods (safe + PUT, DELETE) return fiber.IsMethodIdempotent(c.Method()) }, })) ``` ### Custom Config ```go app.Use(idempotency.New(idempotency.Config{ Lifetime: 42 * time.Minute, // ... })) ``` ## Config Idempotency keys are hidden in logs and error messages by default. Set `DisableValueRedaction` to `true` only when you need to expose them for debugging. | Property | Type | Description | Default | |:--------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------| | Next | `func(fiber.Ctx) bool` | Function to skip this middleware when it returns `true`; use `IsMethodSafe` or `IsMethodIdempotent`. | `func(c fiber.Ctx) bool { return fiber.IsMethodSafe(c.Method()) }` | | Lifetime | `time.Duration` | Maximum lifetime of an idempotency key. | `30 * time.Minute` | | KeyHeader | `string` | Header name containing the idempotency key. | `"X-Idempotency-Key"` | | KeyHeaderValidate | `func(string) error` | Function to validate idempotency header syntax (e.g., UUID). | UUID length check (`36` characters) | | KeepResponseHeaders | `[]string` | List of headers to preserve from original response. | `nil` (keep all headers) | | DisableValueRedaction | `bool` | Disables idempotency key redaction in logs and error messages. | `false` | | Lock | `Locker` | Locks an idempotency key to prevent race conditions. | In-memory locker | | Storage | `fiber.Storage` | Stores response data by idempotency key. | In-memory storage | ## Default Config Values ```go var ConfigDefault = Config{ Next: func(c fiber.Ctx) bool { // Skip middleware for safe methods per RFC 7231 §4.2.2 return fiber.IsMethodSafe(c.Method()) }, Lifetime: 30 * time.Minute, KeyHeader: "X-Idempotency-Key", KeyHeaderValidate: func(k string) error { if l, wl := len(k), 36; l != wl { // UUID length is 36 chars return fmt.Errorf("%w: invalid length: %d != %d", ErrInvalidIdempotencyKey, l, wl) } return nil }, KeepResponseHeaders: nil, Lock: nil, // Set in configDefault so we don't allocate data here. Storage: nil, // Set in configDefault so we don't allocate data here. DisableValueRedaction: false, } ``` --- ## KeyAuth The KeyAuth middleware implements API key authentication. ## Signatures ```go func New(config ...Config) fiber.Handler func TokenFromContext(ctx any) string ``` `TokenFromContext` accepts a `fiber.CustomCtx`, `fiber.Ctx`, a `*fasthttp.RequestCtx`, or a `context.Context`. ## Examples ### Basic example This example registers KeyAuth with an API key stored in a cookie. ```go package main import ( "crypto/sha256" "crypto/subtle" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/fiber/v3/middleware/keyauth" ) var ( apiKey = "correct horse battery staple" ) func validateAPIKey(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { return true, nil } return false, keyauth.ErrMissingOrMalformedAPIKey } func main() { app := fiber.New() // Register middleware before the routes that need it app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromCookie("access_token"), Validator: validateAPIKey, })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) app.Listen(":3000") } ``` **Test:** ```bash # No API key specified -> 401 Missing or invalid API Key curl http://localhost:3000 #> Missing or invalid API Key # Correct API key -> 200 OK curl --cookie "access_token=correct horse battery staple" http://localhost:3000 #> Successfully authenticated! # Incorrect API key -> 401 Missing or invalid API Key curl --cookie "access_token=Clearly A Wrong Key" http://localhost:3000 #> Missing or invalid API Key ``` For a more detailed example, see the [`fiber-envoy-extauthz`](https://github.com/gofiber/recipes/tree/master/fiber-envoy-extauthz) recipe in the `gofiber/recipes` repository. ### Authenticate only certain endpoints Use the `Next` function to run KeyAuth only on selected routes. ```go package main import ( "crypto/sha256" "crypto/subtle" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/fiber/v3/middleware/keyauth" "regexp" "strings" ) var ( apiKey = "correct horse battery staple" protectedURLs = []*regexp.Regexp{ regexp.MustCompile("^/authenticated$"), regexp.MustCompile("^/auth2$"), } ) func validateAPIKey(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { return true, nil } return false, keyauth.ErrMissingOrMalformedAPIKey } func authFilter(c fiber.Ctx) bool { originalURL := strings.ToLower(c.OriginalURL()) for _, pattern := range protectedURLs { if pattern.MatchString(originalURL) { // Run middleware for protected routes return false } } // Skip middleware for non-protected routes return true } func main() { app := fiber.New() app.Use(keyauth.New(keyauth.Config{ Next: authFilter, Extractor: extractors.FromCookie("access_token"), Validator: validateAPIKey, })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome") }) app.Get("/authenticated", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) app.Get("/auth2", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated 2!") }) app.Listen(":3000") } ``` **Test:** ```bash # / doesn't require authentication curl http://localhost:3000 #> Welcome # /authenticated requires authentication curl --cookie "access_token=correct horse battery staple" http://localhost:3000/authenticated #> Successfully authenticated! # /auth2 requires authentication too curl --cookie "access_token=correct horse battery staple" http://localhost:3000/auth2 #> Successfully authenticated 2! ``` ### Apply middleware in the handler You can apply the middleware to specific routes or groups instead of globally. This example uses the default extractor (`FromAuthHeader`). - `FromAuthHeader` expects a compliant [RFC 7235 `token68`](https://datatracker.ietf.org/doc/html/rfc7235#section-2.1) key value. ```go package main import ( "crypto/sha256" "crypto/subtle" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/keyauth" ) const ( apiKey = "my-super-secret-key" ) func main() { app := fiber.New() authMiddleware := keyauth.New(keyauth.Config{ Validator: func(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { return true, nil } return false, keyauth.ErrMissingOrMalformedAPIKey }, }) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome") }) app.Get("/allowed", authMiddleware, func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) app.Listen(":3000") } ``` **Test:** ```bash # / doesn't require authentication curl http://localhost:3000 #> Welcome # /allowed requires authentication curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000/allowed #> Successfully authenticated! ``` ## Key Extractors KeyAuth uses an `Extractor` from the shared [extractors](../guide/extractors) package to retrieve the API key from the request. You can specify one or more extractors in the configuration. For a full list of extractors, chaining, and advanced usage, see the [Extractors Guide](../guide/extractors). ### Typical Usage Specify the extractor in the config. For example, to extract from a cookie: ```go app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromCookie("access_token"), Validator: validateAPIKey, })) ``` To use the default (Authorization header with Bearer scheme): ```go app.Use(keyauth.New(keyauth.Config{ Validator: validateAPIKey, // Extractor defaults to FromAuthHeader("Bearer") })) ``` To try multiple sources (header, then query): ```go app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.Chain( extractors.FromHeader("X-API-Key"), extractors.FromQuery("api_key"), ), Validator: validateAPIKey, })) ``` For custom logic, use `extractors.FromCustom`: ```go app.Use(keyauth.New(keyauth.Config{ Extractor: extractors.FromCustom(func(c fiber.Ctx) (string, error) { return c.Get("X-My-API-Key"), nil }), Validator: validateAPIKey, })) ``` Refer to the [Extractors Guide](../guide/extractors) for details, security notes, and advanced configuration. ## Config | Property | Type | Description | Default | |:----------------|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------|:------------------------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `c.Next()` | | ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. By default a 401 response with a `WWW-Authenticate` challenge is sent. | Default error handler | | Validator | `func(fiber.Ctx, string) (bool, error)` | **Required.** Validator is a function to validate the key. | `nil` (panic) | | Extractor | `extractors.Extractor` | Extractor defines how to retrieve the key from the request. Use helper functions from the shared extractors package, e.g. `extractors.FromAuthHeader("Bearer")` or `extractors.FromCookie("access_token")`. | `extractors.FromAuthHeader("Bearer")` | | Realm | `string` | Realm specifies the protected area name used in the `WWW-Authenticate` header. | `"Restricted"` | | Challenge | `string` | Value of the `WWW-Authenticate` header when no `Authorization` scheme is present. | `ApiKey realm="Restricted"` | | Error | `string` | Error code appended as the `error` parameter in Bearer challenges. Must be `invalid_request`, `invalid_token`, or `insufficient_scope`. | `""` | | ErrorDescription| `string` | Human-readable text for the `error_description` parameter in Bearer challenges. Requires `Error`. | `""` | | ErrorURI | `string` | URI identifying a human-readable web page with information about the `error` in Bearer challenges. Requires `Error` and must be an absolute URI. | `""` | | Scope | `string` | Space-delimited list of scopes for the `scope` parameter in Bearer challenges. Each token must conform to the RFC 6750 `scope-token` syntax and requires `Error` set to `insufficient_scope`. | `""` | ## Default Config ```go var ConfigDefault = Config{ SuccessHandler: func(c fiber.Ctx) error { return c.Next() }, ErrorHandler: func(c fiber.Ctx, _ error) error { return c.Status(fiber.StatusUnauthorized).SendString(ErrMissingOrMalformedAPIKey.Error()) }, Realm: "Restricted", Extractor: extractors.FromAuthHeader("Bearer"), } ``` --- ## Limiter The Limiter middleware for [Fiber](https://github.com/gofiber/fiber) throttles repeated requests to public APIs or endpoints such as password resets. It's also useful for API clients, web crawlers, or other tasks that need rate limiting. Limiter redacts request keys in error paths by default so storage identifiers and rate-limit keys don't leak into logs. Set `DisableValueRedaction` to `true` when you explicitly need the raw key for troubleshooting. :::note This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. ::: :::note This module does not share state with other processes/servers by default. ::: ## Signatures ```go func New(config ...Config) fiber.Handler type Handler interface { New(config *Config) fiber.Handler } ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/limiter" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(limiter.New()) // Or extend your config for customization app.Use(limiter.New(limiter.Config{ Next: func(c fiber.Ctx) bool { return c.IP() == "127.0.0.1" }, Max: 20, MaxFunc: func(c fiber.Ctx) int { return 20 }, Expiration: 30 * time.Second, ExpirationFunc: func(c fiber.Ctx) time.Duration { // Use longer expiration for sensitive endpoints if c.Path() == "/login" { return 60 * time.Second } return 30 * time.Second }, KeyGenerator: func(c fiber.Ctx) string { return c.Get("x-forwarded-for") }, LimitReached: func(c fiber.Ctx) error { return c.SendFile("./toofast.html") }, Storage: myCustomStorage{}, })) ``` ## Sliding window Instead of using the standard fixed window algorithm, you can enable the [sliding window](https://en.wikipedia.org/wiki/Sliding_window_protocol) algorithm. An example configuration is: ```go app.Use(limiter.New(limiter.Config{ Max: 20, Expiration: 30 * time.Second, LimiterMiddleware: limiter.SlidingWindow{}, })) ``` Each new window also considers the previous one (if any). The rate is calculated as: ```text weightOfPreviousWindow = previousWindowRequests * (elapsedInCurrentWindow / Expiration) rate = weightOfPreviousWindow + currentWindowRequests ``` ## Dynamic limit You can also calculate the limit dynamically using the `MaxFunc` parameter. It receives the request context and allows you to compute a different limit for each request. Example: ```go app.Use(limiter.New(limiter.Config{ MaxFunc: func(c fiber.Ctx) int { return getUserLimit(ctx.Param("id")) }, Expiration: 30 * time.Second, })) ``` ## Dynamic expiration You can also calculate the expiration dynamically using the `ExpirationFunc` parameter. It receives the request context and allows you to set a different expiration window for each request. Window accounting is whole-second, so a positive duration below one second is treated as a one-second window. A zero or negative duration falls back to the default expiration. Example: ```go app.Use(limiter.New(limiter.Config{ Max: 20, ExpirationFunc: func(c fiber.Ctx) time.Duration { return getExpirationForRoute(c.Path()) }, })) ``` ## Config | Property | Type | Description | Default | |:-----------------------|:--------------------------|:--------------------------------------------------------------------------------------------|:-----------------------------------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Max | `int` | Maximum number of recent connections within `Expiration` seconds before sending a 429 response. | 5 | | MaxFunc | `func(fiber.Ctx) int` | Function that calculates the maximum number of recent connections within `Expiration` seconds before sending a 429 response. | A function that returns `cfg.Max` | | KeyGenerator | `func(fiber.Ctx) string` | Function to generate custom keys; uses `c.IP()` by default. | A function using `c.IP()` as the default | | Expiration | `time.Duration` | Duration to keep request records in memory. | 1 * time.Minute | | ExpirationFunc | `func(fiber.Ctx) time.Duration` | Function that calculates the expiration duration dynamically. Positive values below one second are floored to one second; non-positive values fall back to the default expiration. | A function that returns `cfg.Expiration` | | LimitReached | `fiber.Handler` | Called when a request exceeds the limit. | A function sending a 429 response | | SkipFailedRequests | `bool` | When set to `true`, requests with status code ≥ 400 aren't counted. | false | | SkipSuccessfulRequests | `bool` | When set to `true`, requests with status code < 400 aren't counted. | false | | DisableHeaders | `bool` | When set to `true`, the middleware omits rate limit headers (`X-RateLimit-*` and `Retry-After`). | false | | DisableValueRedaction | `bool` | Disables redaction of limiter keys in error messages and logs. | false | | Storage | `fiber.Storage` | Persists middleware state. | An in-memory store for this process only | | LimiterMiddleware | `limiter.Handler` | Selects the algorithm implementation. Implementations now receive a pointer to the active config when their `New` method is invoked. | A new Fixed Window Rate Limiter | :::note A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. ::: ## Default Config ```go var ConfigDefault = Config{ Max: 5, MaxFunc: func(c fiber.Ctx) int { return 5 }, Expiration: 1 * time.Minute, // ExpirationFunc defaults to nil and is set dynamically to return cfg.Expiration KeyGenerator: func(c fiber.Ctx) string { return c.IP() }, LimitReached: func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTooManyRequests) }, SkipFailedRequests: false, SkipSuccessfulRequests: false, DisableHeaders: false, DisableValueRedaction: false, LimiterMiddleware: FixedWindow{}, } ``` ### Custom Storage/Database You can use any storage from our [storage](https://github.com/gofiber/storage/) package. ```go storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3/v2 app.Use(limiter.New(limiter.Config{ Storage: storage, })) ``` --- ## Logger Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP requests and responses. ## Signatures ```go func New(config ...Config) fiber.Handler func RegisterTag(tag string, fn LogFunc) error func MustRegisterTag(tag string, fn LogFunc) ``` ## Examples Import the package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/requestid" ) ``` :::tip Registration order matters: only routes added after the logger are logged, so register it early. ::: Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(logger.New()) // Or extend your config for customization // Log remote IP and port app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) // Logging Request ID app.Use(requestid.New()) // Ensure requestid middleware is used before the logger app.Use(logger.New(logger.Config{ // requestid.New() registers ${requestid} automatically. Format: "${pid} ${requestid} ${status} - ${method} ${path}\n", })) // Changing TimeZone & TimeFormat app.Use(logger.New(logger.Config{ Format: "${pid} ${status} - ${method} ${path}\n", TimeFormat: "02-Jan-2006", TimeZone: "America/New_York", })) // Custom File Writer accessLog, err := os.OpenFile("./access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatalf("error opening access.log file: %v", err) } defer accessLog.Close() app.Use(logger.New(logger.Config{ Stream: accessLog, })) // Add Custom Tags app.Use(logger.New(logger.Config{ CustomTags: map[string]logger.LogFunc{ "custom_tag": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) { return output.WriteString("it is a custom tag") }, }, })) // Callback after log is written app.Use(logger.New(logger.Config{ TimeFormat: time.RFC3339Nano, TimeZone: "Asia/Shanghai", Done: func(c fiber.Ctx, logString []byte) { if c.Response().StatusCode() != fiber.StatusOK { reporter.SendToSlack(logString) } }, })) // Disable colors when outputting to default format app.Use(logger.New(logger.Config{ DisableColors: true, })) // Force the use of colors app.Use(logger.New(logger.Config{ ForceColors: true, })) // Use predefined formats app.Use(logger.New(logger.Config{ Format: logger.CommonFormat, })) app.Use(logger.New(logger.Config{ Format: logger.CombinedFormat, })) app.Use(logger.New(logger.Config{ Format: logger.JSONFormat, })) app.Use(logger.New(logger.Config{ Format: logger.ECSFormat, })) ``` ### Auto-Registered Tags Some Fiber middleware registers logger middleware tags automatically. Register the producing middleware before `logger.New()` and then use the tag in `Format`. ```go app.Use(requestid.New()) app.Use(logger.New(logger.Config{ Format: "${requestid} ${status} ${method} ${path}\n", })) ``` The logger middleware resolves tags in this order: 1. Built-in logger tags, such as `${method}`, `${path}`, and `${status}`. 2. Globally registered tags from Fiber middleware or `logger.RegisterTag`. 3. `Config.CustomTags`, which override tags with the same name for that logger instance. The following tags are registered by Fiber middleware when the middleware is initialized: | Tag | Registered by | Value | | :-- | :------------ | :---- | | `${requestid}` / `${request-id}` | `requestid.New()` | Request ID stored by the requestid middleware. | | `${username}` | `basicauth.New()` | Authenticated username stored by the basicauth middleware. | | `${api-key}` | `keyauth.New()` | Redacted API key stored by the keyauth middleware. | | `${csrf-token}` | `csrf.New()` | Redacted marker when the csrf middleware stores a token. | | `${session-id}` | `session.New()` or `session.NewWithStore()` | Redacted session ID stored by the session middleware. | :::note Auto-registered tags are access-log tags for `middleware/logger`. The same names are also registered for application logs in the `log` package via `logger.RegisterContextTag` — see [api/log#context-tags](../api/log.md#context-tags) for details on `log.WithContext` enrichment. ::: ### Register Tags from Custom Middleware Third-party middleware can expose logger tags with `logger.RegisterTag` or `logger.MustRegisterTag`. Use `sync.Once` so the tag is registered once even when the middleware is initialized multiple times. A tag you register replaces the built-in renderer, so it does not inherit the [control-character scrubbing](#control-character-sanitization) the built-in tags apply. Wrap request-derived values in `logger.SanitizeValue`. ```go package tenantmw import ( "sync" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" ) type tenantContextKey struct{} var tenantKey tenantContextKey var registerLoggerTagsOnce sync.Once func New() fiber.Handler { registerLoggerTagsOnce.Do(func() { logger.MustRegisterTag("tenant", func(output logger.Buffer, c fiber.Ctx, _ *logger.Data, _ string) (int, error) { tenant, _ := fiber.ValueFromContext[string](c, tenantKey) return output.WriteString(tenant) }) }) return func(c fiber.Ctx) error { fiber.StoreInContext(c, tenantKey, "acme") return c.Next() } } ``` Use the registered tag in the logger format after installing the middleware: ```go app.Use(tenantmw.New()) app.Use(logger.New(logger.Config{ Format: "${tenant} ${status} ${method} ${path}\n", })) ``` Use `Config.CustomTags` when one logger instance needs a local override without changing the global tag registration. These also bypass the built-in [control-character scrubbing](#control-character-sanitization) — wrap request-derived values in `logger.SanitizeValue`: ```go app.Use(logger.New(logger.Config{ Format: "${tenant} ${status} ${method} ${path}\n", CustomTags: map[string]logger.LogFunc{ "tenant": func(output logger.Buffer, c fiber.Ctx, _ *logger.Data, _ string) (int, error) { return output.WriteString("override") }, }, })) ``` ### Use Logger Middleware with Other Loggers To combine the logger middleware with loggers like Zerolog, Zap, or Logrus, use the `LoggerToWriter` helper to adapt them to an `io.Writer`. ```go package main import ( "github.com/gofiber/contrib/fiberzap/v2" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" "github.com/gofiber/fiber/v3/middleware/logger" ) func main() { // Create a new Fiber instance app := fiber.New() // Create a new zap logger which is compatible with Fiber AllLogger interface zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ ExtraKeys: []string{"request_id"}, }) // Use the logger middleware with the zap logger app.Use(logger.New(logger.Config{ Stream: logger.LoggerToWriter(zap, log.LevelDebug), })) // Define a route app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) // Start server on http://localhost:3000 app.Listen(":3000") } ``` :::tip Writing to `os.File` is goroutine-safe, but custom streams may require locking to serialize writes. ::: ## Config | Property | Type | Description | Default | | :------------ | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Skip | `func(fiber.Ctx) bool` | Skip is a function to determine if logging is skipped or written to Stream. | `nil` | | Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Stream, and pass the log string as parameter. | `nil` | | CustomTags | `map[string]LogFunc` | Defines custom tag actions for this logger instance. These tags override built-in and globally registered tags with the same name. | `nil` | | `Format` | `string` | Defines the logging tags. See more in [Predefined Formats](#predefined-formats), or create your own using [Tags](#constants). | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` (same as `DefaultFormat`) | | TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | | TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | | TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | | Stream | `io.Writer` | Stream is a writer where logs are written. | `os.Stdout` | | TimeDone | `<-chan struct{}` | TimeDone stops the background timestamp updater when it is closed. | `nil` | | BeforeHandlerFunc | `func(*Config)` | BeforeHandlerFunc runs once before the handler is built, letting you customize colors, template, etc. | `beforeHandlerFunc` | | LoggerFunc | `func(c fiber.Ctx, data *Data, cfg *Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance` | | DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | | ForceColors | `bool` | ForceColors defines if the logs output should be colorized even when the output is not a terminal. | `false` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Skip: nil, Done: nil, Format: DefaultFormat, TimeFormat: "15:04:05", TimeZone: "Local", TimeInterval: 500 * time.Millisecond, Stream: os.Stdout, BeforeHandlerFunc: beforeHandlerFunc, LoggerFunc: defaultLoggerInstance, enableColors: true, } ``` ## Predefined Formats Logger provides predefined formats that you can use by name or directly by specifying the format string. | **Format Constant** | **Format String** | **Description** | |---------------------|--------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| | `DefaultFormat` | `"[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n"` | Fiber's default logger format. | | `CommonFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}\n"` | Common Log Format (CLF) used in web server logs. | | `CombinedFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent} "${referer}" "${ua}"\n"` | CLF format plus the `referer` and `user agent` fields. | | `JSONFormat` | `"{\"time\":\"${time}\",\"ip\":\"${ip}\",\"method\":\"${method}\",\"url\":\"${url}\",\"status\":${status},\"bytesSent\":${bytesSent}}\n"` | JSON format for structured logging. | | `ECSFormat` | `"{\"@timestamp\":\"${time}\",\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":\"${ip}\"},\"http\":{\"request\":{\"method\":\"${method}\",\"url\":\"${url}\",\"protocol\":\"${protocol}\"},\"response\":{\"status_code\":${status},\"body\":{\"bytes\":${bytesSent}}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":\"${method} ${url} responded with ${status}\"}\n"` | Elastic Common Schema (ECS) format for structured logging. | :::tip `${bytesSent}` returns the value of the `Content-Length` response header. If the header is missing or the response is streaming (e.g., chunked encoding), the value will be `-1`. Fiber does not calculate the actual response body size for performance reasons. ::: ## Control-Character Sanitization Values that come from the request are scrubbed before they reach the log stream: every ASCII control byte (C0 and DEL) is replaced with a space, and horizontal tab is preserved. Without this, a percent-decoded query parameter, form field, or request body containing `\r\n` could forge additional access-log lines and corrupt an audit trail. Scrubbing covers the default format as well as these tags: `${path}` `${url}` `${ua}` `${referer}` `${ip}` `${ips}` `${host}` `${scheme}` `${route}` `${body}` `${resBody}` `${reqHeaders}` `${queryParams}` `${error}` `${reqHeader:}` `${respHeader:}` `${query:}` `${form:}` `${cookie:}` `${locals:}` Tags whose values the framework controls — `${status}`, `${method}`, `${protocol}`, `${port}`, `${latency}`, `${pid}`, `${time}`, `${bytesSent}`, `${bytesReceived}` and the color tags — are written unchanged. `${method}` and `${protocol}` come from the request line, which fasthttp rejects outright if it holds a control byte. Only ASCII controls are replaced. Bytes at or above `0x80` pass through untouched, so C1 controls (U+0080–U+009F, including NEL U+0085, which some log pipelines treat as a line break) survive scrubbing. Handle those yourself if your values can carry them. :::caution Four paths bypass the built-in scrubbing, because each one replaces the renderer rather than wrapping it: - `Config.CustomTags` - `RegisterTag` / `MustRegisterTag` - `RegisterContextTag` - `Config.LoggerFunc`, which replaces the rendering pipeline wholesale Anything request-derived that you write from one of these needs scrubbing. Use `logger.SanitizeValue`, which applies exactly what the built-in tags apply: ```go logger.MustRegisterTag("tenant", func(output logger.Buffer, c fiber.Ctx, _ *logger.Data, _ string) (int, error) { return output.WriteString(logger.SanitizeValue(c.Get("X-Tenant-ID"))) }) ``` Fiber's own context tags — `${username}`, `${api-key}`, `${csrf-token}`, `${requestid}`, `${session-id}` — are safe because the middleware behind each one validates or redacts at the source, not because `RegisterContextTag` scrubs. ::: ## Constants ```go // Logger variables const ( TagPid = "pid" TagTime = "time" TagReferer = "referer" TagProtocol = "protocol" TagScheme = "scheme" TagPort = "port" TagIP = "ip" TagIPs = "ips" TagHost = "host" TagMethod = "method" TagPath = "path" TagURL = "url" TagUA = "ua" TagLatency = "latency" TagStatus = "status" // response status TagResBody = "resBody" // response body TagReqHeaders = "reqHeaders" TagQueryStringParams = "queryParams" // request query parameters TagBody = "body" // request body TagBytesSent = "bytesSent" TagBytesReceived = "bytesReceived" TagRoute = "route" TagError = "error" TagReqHeader = "reqHeader:" // request header TagRespHeader = "respHeader:" // response header TagQuery = "query:" // request query TagForm = "form:" // request form TagCookie = "cookie:" // request cookie TagLocals = "locals:" // colors TagBlack = "black" TagRed = "red" TagGreen = "green" TagYellow = "yellow" TagBlue = "blue" TagMagenta = "magenta" TagCyan = "cyan" TagWhite = "white" TagReset = "reset" ) ``` --- ## Paginate Pagination middleware for [Fiber](https://github.com/gofiber/fiber) that extracts pagination parameters from query strings and stores them in the request context. Supports page-based, offset-based, and cursor-based pagination with multi-field sorting. ## Signatures ```go func New(config ...Config) fiber.Handler func FromContext(ctx any) (*PageInfo, bool) ``` `FromContext` accepts `fiber.CustomCtx`, `fiber.Ctx`, `*fasthttp.RequestCtx`, or `context.Context`. ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/paginate" ) ``` Once your Fiber app is initialized, choose one of the following approaches: ### Basic Usage ```go app.Use(paginate.New()) app.Get("/users", func(c fiber.Ctx) error { pageInfo, ok := paginate.FromContext(c) if !ok { return fiber.ErrBadRequest } // Use pageInfo.Page, pageInfo.Limit, pageInfo.Start() // GET /users?page=2&limit=20 → Page: 2, Limit: 20, Start(): 20 return c.JSON(pageInfo) }) ``` ### Sorting ```go app.Use(paginate.New(paginate.Config{ SortKey: "sort", DefaultSort: "id", AllowedSorts: []string{"id", "name", "created_at"}, })) // GET /users?sort=name,-created_at // → Sort: [{Field: "name", Order: "asc"}, {Field: "created_at", Order: "desc"}] ``` ### Cursor Pagination ```go app.Use(paginate.New()) app.Get("/feed", func(c fiber.Ctx) error { pageInfo, ok := paginate.FromContext(c) if !ok { return fiber.ErrBadRequest } if pageInfo.Cursor != "" { // Decode the cursor to get keyset values values := pageInfo.CursorValues() // Use values["id"], values["created_at"], etc. for WHERE clause } // results is a slice of items from your database query // After fetching results, set the next cursor for the client if len(results) > 0 { lastItem := results[len(results)-1] if err := pageInfo.SetNextCursor(map[string]any{ "id": lastItem.ID, "created_at": lastItem.CreatedAt, }); err != nil { return err } } return c.JSON(fiber.Map{ "data": results, "has_more": pageInfo.HasMore, "next_cursor": pageInfo.NextCursor, }) }) // First request: GET /feed?limit=20 // Next request: GET /feed?cursor=&limit=20 ``` ### Custom Configuration ```go app.Use(paginate.New(paginate.Config{ PageKey: "p", LimitKey: "size", DefaultPage: 1, DefaultLimit: 25, SortKey: "order_by", DefaultSort: "created_at", AllowedSorts: []string{"created_at", "name", "email"}, CursorKey: "after", CursorParam: "starting_after", })) ``` ## Config | Property | Type | Description | Default | |:-------------|:-------------------------|:-------------------------------------------------------------------|:-----------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | PageKey | `string` | Query string key for page number. | `"page"` | | DefaultPage | `int` | Default page number. | `1` | | LimitKey | `string` | Query string key for limit. | `"limit"` | | DefaultLimit | `int` | Default items per page. | `10` | | SortKey | `string` | Query string key for sort. | `""` | | DefaultSort | `string` | Default sort field. | `"id"` | | AllowedSorts | `[]string` | Whitelist of allowed sort fields. If nil or empty, request sort fields are ignored and the default sort is used. | `nil` | | OffsetKey | `string` | Query string key for offset. | `"offset"` | | CursorKey | `string` | Query string key for cursor-based pagination. | `"cursor"` | | CursorParam | `string` | Optional alias for the cursor query key. | `""` | | MaxLimit | `int` | Maximum items per page. | `100` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, PageKey: "page", DefaultPage: 1, LimitKey: "limit", DefaultLimit: 10, MaxLimit: 100, DefaultSort: "id", OffsetKey: "offset", CursorKey: "cursor", } ``` ## PageInfo The `PageInfo` struct is stored in the request context and provides: | Method | Description | |:------------------------------------------------|:---------------------------------------------------------------| | `Start() int` | Returns calculated start index (from page/limit or offset) | | `SortBy(field, order)` | Adds a sort field (chainable) | | `NextPageURL(baseURL)` | Generates next page URL with default keys | | `NextPageURLWithKeys(baseURL, pageKey, limitKey)` | Generates next page URL with custom query keys | | `PreviousPageURL(baseURL)` | Generates previous page URL (empty on page 1) | | `PreviousPageURLWithKeys(baseURL, pageKey, limitKey)` | Generates previous page URL with custom query keys | | `NextCursorURL(baseURL)` | Generates next cursor URL (empty if no more) | | `NextCursorURLWithKeys(baseURL, cursorKey, limitKey)` | Generates next cursor URL with custom query keys | | `CursorValues()` | Decodes cursor token into key-value map | | `SetNextCursor(values)` | Encodes values into cursor token, sets HasMore; returns error | ## Safety - Limit is capped at `MaxLimit` (default: 100, configurable) to prevent excessive memory usage - Page values below 1 reset to 1 - Negative offsets reset to 0 - Sort fields are validated against `AllowedSorts` - Cursor tokens exceeding 2048 characters are rejected with `400 Bad Request` - `SetNextCursor` returns an error if the encoded token would exceed 2048 characters, preventing the server from issuing cursors it would later reject - Invalid cursor tokens return `400 Bad Request` via Fiber's error handler - If `DefaultSort` is not included in `AllowedSorts`, it falls back to the first allowed sort field - URL helpers preserve existing query parameters when building pagination links --- ## Pprof Pprof middleware exposes runtime profiling data for analysis with the Go `pprof` tool. Importing it registers handlers under `/debug/pprof/`. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/pprof" ) ``` Once your Fiber app is initialized, use the middleware as shown: ```go // Initialize default config app.Use(pprof.New()) // Or customize the config // For multi-ingress systems, add a URL prefix: app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) // The resulting URL is "/endpoint-prefix/debug/pprof/" ``` ## Config | Property | Type | Description | Default | |:---------|:------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------:| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Prefix | `string` | Prefix adds a segment before `/debug/pprof`; it must start with a slash and omit the trailing slash. Example: `/federated-fiber` | `""` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, } ``` --- ## Proxy The Proxy middleware forwards requests to one or more upstream servers. ## Signatures ```go // Balancer creates a load balancer among multiple upstream servers. func Balancer(config ...Config) fiber.Handler // Forward performs the given http request and fills the given http response. func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler // Do performs the given http request and fills the given http response. func Do(c fiber.Ctx, addr string, clients ...*fasthttp.Client) error // DoRedirects performs the given http request and fills the given http response while following up to maxRedirectsCount redirects. func DoRedirects(c fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error // DoDeadline performs the given request and waits for response until the given deadline. func DoDeadline(c fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error // DoTimeout performs the given request and waits for response during the given timeout duration. func DoTimeout(c fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error // DomainForward performs the given http request based on the provided domain and fills the given http response. func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler // BalancerForward performs the given http request based round robin balancer and fills the given http response. func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler ``` ## Security The proxy middleware applies several defenses by default. They can be relaxed via `Config.SecurityPolicy` (for `Balancer`) or `proxy.WithSecurityPolicy` (for the runtime helpers `Do`, `Forward`, `DoRedirects`, `DoTimeout`, `DoDeadline`). ### SSRF protection Upstream addresses that resolve to loopback, RFC 1918 private, link-local (including the `169.254.169.254` cloud-metadata address), multicast, unspecified, or RFC 6598 CGNAT ranges are rejected with `ErrUpstreamHostBlocked`. If any resolved IP falls in a blocked range the upstream is rejected, mitigating DNS-rebinding attempts that return a mix of public and private answers. For `Balancer`, the resolved IP is re-validated at **dial time** (via a guarded `Dial` on each upstream `fasthttp.HostClient`), which both defeats DNS-rebinding and avoids resolving hostnames at startup — a transient DNS failure won't panic your application. DNS lookups are bounded by a 5-second timeout. :::caution DNS-rebinding scope The dial-time re-validation only applies to `Balancer`, because those `HostClient`s are constructed by the middleware. The runtime helpers — `Do`, `DoRedirects`, `DoTimeout`, `DoDeadline`, `Forward`, `DomainForward`, and `BalancerForward` — validate the upstream host up front, then dispatch through the shared or user-supplied `*fasthttp.Client`, which re-resolves the name without the guard. Against a **rebinding-capable resolver** these paths have a check/use window and are not fully mitigated. If that is part of your threat model, use `Balancer` (with `AllowPrivateIPs = false`), or supply a client whose `Dial` performs its own resolved-IP validation. ::: Set `SecurityPolicy.AllowPrivateIPs = true` to opt out — required when proxying to internal services on the same network. ### Scheme allowlist Only `http` and `https` upstream schemes are accepted by default; `file://`, `gopher://`, `ftp://`, and other schemes are rejected. Override via `SecurityPolicy.AllowedSchemes`. ### HTTPS-to-HTTP redirect downgrades `DoRedirects` rejects redirects from HTTPS origins to plaintext HTTP targets with `ErrRedirectDowngrade`. Following such a redirect would leak any cookies or `Authorization` headers established under TLS. Set `SecurityPolicy.AllowHTTPSDowngrade = true` to override. When a redirect crosses to a **different host**, `DoRedirects` strips the `Authorization`, `Proxy-Authorization`, and `Cookie` headers so credentials bound to the original origin are not forwarded to a third-party upstream. Same-host redirects retain these headers. ### RFC 7230 hop-by-hop header stripping `Connection`, `Keep-Alive`, `Proxy-Authenticate`, `Proxy-Authorization`, `TE`, `Trailer`, `Transfer-Encoding`, and `Upgrade` are stripped from both the outbound request and the inbound response, along with every header listed in the `Connection` field per RFC 7230 §6.1. This prevents request smuggling (`TE`/`Transfer-Encoding`), proxy-credential forwarding, and protocol-upgrade leaks. The legacy `KeepConnectionHeader` option preserves only the literal `Connection` header for backwards compatibility; the other hop-by-hop headers are still stripped. To preserve every hop-by-hop header (not recommended), set `SecurityPolicy.KeepHopByHopHeaders = true`. ### TLS minimum version `Config.TLSConfig` is cloned with `MinVersion: tls.VersionTLS12` if no minimum is configured, so deprecated TLS versions cannot be negotiated by accident. ### Response body size and connection caps `Config.MaxResponseBodySize` bounds upstream response bodies to protect against memory exhaustion. `Config.MaxConnsPerHost` (default `1024`) caps concurrent connections per upstream to limit fan-out from a single hot host. ### X-Real-IP spoof prevention `Forward`, `DomainForward`, and `BalancerForward` automatically overwrite the `X-Real-IP` header with `c.IP()` before forwarding, so clients cannot spoof their address. `DomainForward` only applies the overwrite when the request host matches the configured hostname (matched case-insensitively per RFC 9110 §4.2.3); non-matching requests are passed through unchanged. If you're using `Balancer` with the `Config` struct, you can replicate the protection in `ModifyRequest`. When using `Do`, `DoRedirects`, `DoDeadline`, or `DoTimeout` directly, the `X-Real-IP` header is not set automatically — set it manually if needed: ```go c.Request().Header.Set("X-Real-IP", c.IP()) ``` ### Path concatenation safety `DomainForward` and `BalancerForward` previously concatenated the configured upstream with `c.OriginalURL()`. Crafted request paths beginning with `//` could exploit URL parsing to redirect the proxy at a different host (network-path reference injection). The proxy now sanitises the joined path so the upstream host pinned in configuration is preserved regardless of the inbound request. ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/proxy" ) ``` Once your Fiber app is initialized, you can use the middleware as shown: ```go // Use proxy.WithClient to set a global custom client. proxy.WithClient(&fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, MaxConnsPerHost: 2048, // Allow self-signed certificates when proxying to HTTPS targets. // SECURITY: disables certificate verification — use only when the // upstream is on a trusted network. TLSConfig: &tls.Config{ InsecureSkipVerify: true, MinVersion: tls.VersionTLS12, }, }) // Relax SSRF protection for local development against loopback servers. // SECURITY: in production, leave AllowPrivateIPs false (the default) and // list explicit upstream hosts so the proxy cannot be coerced into // reaching internal services or cloud-metadata endpoints. prev := proxy.WithSecurityPolicy(proxy.SecurityPolicy{ AllowedSchemes: []string{"http", "https"}, AllowPrivateIPs: true, }) defer proxy.WithSecurityPolicy(prev) // Forward requests for a specific domain with proxy.DomainForward. app.Get("/payments", proxy.DomainForward("docs.gofiber.io", "http://localhost:8000")) // Forward to a URL using a custom client app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, })) // Make a proxied request within a handler app.Get("/:id", func(c fiber.Ctx) error { url := "https://i.imgur.com/" + c.Params("id") + ".gif" if err := proxy.Do(c, url); err != nil { return err } // Remove Server header from response c.Response().Header.Del(fiber.HeaderServer) return nil }) // Proxy requests while following redirects app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoRedirects(c, "http://google.com", 3); err != nil { return err } // Remove Server header from response c.Response().Header.Del(fiber.HeaderServer) return nil }) // Proxy requests and wait up to five seconds before timing out app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoTimeout(c, "http://localhost:3000", time.Second * 5); err != nil { return err } // Remove Server header from response c.Response().Header.Del(fiber.HeaderServer) return nil }) // Proxy requests with a deadline one minute from now app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { return err } // Remove Server header from response c.Response().Header.Del(fiber.HeaderServer) return nil }) // Minimal round-robin balancer app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ "http://localhost:3001", "http://localhost:3002", "http://localhost:3003", }, })) // Keep the Connection header when proxying app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ "http://localhost:3001", }, KeepConnectionHeader: true, })) // Or extend your balancer for customization app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ "http://localhost:3001", "http://localhost:3002", "http://localhost:3003", }, MaxConnsPerHost: 2048, ModifyRequest: func(c fiber.Ctx) error { c.Request().Header.Set("X-Real-IP", c.IP()) return nil }, ModifyResponse: func(c fiber.Ctx) error { c.Response().Header.Del(fiber.HeaderServer) return nil }, })) // Or this way if the balancer is using https and the destination server is only using http. app.Use(proxy.BalancerForward([]string{ "http://localhost:3001", "http://localhost:3002", "http://localhost:3003", })) // Make round robin balancer with IPv6 support. app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ "http://[::1]:3001", "http://127.0.0.1:3002", "http://localhost:3003", }, // Enable TCP4 and TCP6 network stacks. DialDualStack: true, })) ``` ## Config | Property | Type | Description | Default | |:----------------|:-----------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | Servers | `[]string` | Servers defines a list of `://` HTTP servers, which are used in a round-robin manner. i.e.: "[https://foobar.com](https://foobar.com), [http://www.foobar.com](http://www.foobar.com)" | (Required) | | ModifyRequest | `fiber.Handler` | ModifyRequest allows you to alter the request. | `nil` | | ModifyResponse | `fiber.Handler` | ModifyResponse allows you to alter the response. | `nil` | | Timeout | `time.Duration` | Timeout is the request timeout used when calling the proxy client. | 1 second | | MaxConnsPerHost | `int` | Maximum number of connections per upstream host. The default proxy client and balancer host clients use this limit unless you override it with `WithClient`, a per-handler client, or `proxy.Config`. | `1024` | | ReadBufferSize | `int` | Per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers (for example, BIG cookies). | (Not specified) | | WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | (Not specified) | | KeepConnectionHeader | `bool` | Keeps the `Connection` header when set to `true`. By default the header is removed to comply with RFC 7230 §6.1 and avoid proxy loops. Other hop-by-hop headers are still stripped regardless of this setting. | `false` | | TLSConfig | `*tls.Config` | TLS config for the HTTP client. Cloned with `MinVersion: tls.VersionTLS12` when no minimum is set. | `nil` | | DialDualStack | `bool` | Client will attempt to connect to both IPv4 and IPv6 host addresses if set to true. | `false` | | Client | `*fasthttp.LBClient` | Client is a custom client when client config is complex. | `nil` | | SecurityPolicy | `*SecurityPolicy` | Overrides the default SSRF, redirect, and hop-by-hop header rules for this balancer. When `nil`, the package-level policy set via `WithSecurityPolicy` is used. See [Security](#security). | `nil` | | MaxResponseBodySize | `int` | Maximum upstream response body size in bytes. `0` keeps fasthttp's unlimited default. | `0` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, ModifyRequest: nil, ModifyResponse: nil, MaxConnsPerHost: 1024, Timeout: fasthttp.DefaultLBClientTimeout, KeepConnectionHeader: false, } ``` ## Default SecurityPolicy When `Config.SecurityPolicy` is `nil` (and `proxy.WithSecurityPolicy` has not been called), the package falls back to the value returned by `proxy.DefaultSecurityPolicy()`: ```go // DefaultSecurityPolicy returns the secure-by-default policy. func DefaultSecurityPolicy() proxy.SecurityPolicy { return proxy.SecurityPolicy{ AllowedSchemes: []string{"http", "https"}, AllowPrivateIPs: false, AllowHTTPSDowngrade: false, KeepHopByHopHeaders: false, } } ``` --- ## Recover The Recover middleware for [Fiber](https://github.com/gofiber/fiber) intercepts panics and forwards them to the central [ErrorHandler](../guide/error-handling). ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" recoverer "github.com/gofiber/fiber/v3/middleware/recover" ) ``` Once your Fiber app is initialized, use the middleware like this: ```go // Initialize default config app.Use(recoverer.New()) // Panics in subsequent handlers are caught by the middleware app.Get("/", func(c fiber.Ctx) error { panic("I'm an error") }) ``` ## Config | Property | Type | Description | Default | |:------------------|:-----------------------------|:------------------------------------------------------|:---------------------------| | Next | `func(fiber.Ctx) bool` | Skip when the function returns `true`. | `nil` | | PanicHandler | `func(fiber.Ctx, any) error` | Customize the error returned from a recovered panic. | `DefaultPanicHandler` | | EnableStackTrace | `bool` | Capture and include a stack trace in error responses. | `false` | | StackTraceHandler | `func(fiber.Ctx, any)` | Handle the captured stack trace when enabled. | `defaultStackTraceHandler` | ## Default Config ```go var ConfigDefault = recoverer.Config{ Next: nil, PanicHandler: DefaultPanicHandler, StackTraceHandler: defaultStackTraceHandler, EnableStackTrace: false, } // Set up a PanicHandler to hide internals. app.Use(recoverer.New(recoverer.Config{PanicHandler: func(c fiber.Ctx, r any) error { return fiber.ErrInternalServerError }})) // In more elaborate scenarios you can also create a custom error which can be processed differently in the fiber.ErrorHandler. // See the tests for an example of such an ErrorHandler. // You could also just wrap the default handler's error, e.g. fmt.Errorf("[RECOVERED]: %w", recoverer.DefaultPanicHandler(c, r)) app.Use(recoverer.New(recoverer.Config{PanicHandler: func(c fiber.Ctx, r any) error { return &MyCustomRecoveredFromPanicError { Inner: recoverer.DefaultPanicHandler(c, r), } }})) ``` --- ## Redirect Redirect middleware maps old URLs to new ones using simple rules. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/redirect" ) func main() { app := fiber.New() app.Use(redirect.New(redirect.Config{ Rules: map[string]string{ "/old": "/new", "/old/*": "/new/$1", }, StatusCode: fiber.StatusMovedPermanently, })) app.Get("/new", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Get("/new/*", func(c fiber.Ctx) error { return c.SendString("Wildcard: " + c.Params("*")) }) app.Listen(":3000") } ``` ## Test ```bash curl http://localhost:3000/old curl http://localhost:3000/old/hello ``` ## Config | Property | Type | Description | Default | |:-----------|:--------------------|:------------------------------------------|:-----------------------| | Next | `func(fiber.Ctx) bool` | Skip when function returns true. | nil | | Rules | `map[string]string` | Map paths to new ones; `$1`, `$2` insert params. | Required | | StatusCode | `int` | HTTP code for redirects. | 302 Temporary Redirect | ## Default Config ```go var ConfigDefault = Config{ StatusCode: fiber.StatusFound, } ``` --- ## RequestID The RequestID middleware generates or propagates a request identifier, adding it to the response headers and request context. ## Signatures ```go func New(config ...Config) fiber.Handler func FromContext(ctx any) string ``` `FromContext` accepts a `fiber.CustomCtx`, `fiber.Ctx`, a `*fasthttp.RequestCtx`, or a `context.Context`. ## Examples Import the middleware package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/requestid" ) ``` Once your Fiber app is initialized, add the middleware like this: ```go // Initialize default config app.Use(requestid.New()) // Or extend your config for customization app.Use(requestid.New(requestid.Config{ Header: "X-Custom-Header", Generator: func() string { return "static-id" }, })) ``` If the request already includes the configured header, that value is reused instead of generating a new one. The middleware rejects IDs containing characters outside the visible ASCII range (for example, control characters or obs-text bytes) and will regenerate the value using up to three attempts from the configured generator (or SecureToken when no generator is set). When a custom generator fails to produce a valid ID, the middleware falls back to SecureToken to keep headers RFC-compliant across transports. Retrieve the request ID ```go func handler(c fiber.Ctx) error { id := requestid.FromContext(c) log.Printf("Request ID: %s", id) return c.SendString("Hello, World!") } ``` ## Config | Property | Type | Description | Default | |:----------|:---------------------|:-----------------------------------------|:---------------| | Next | `func(fiber.Ctx) bool` | Skip when the function returns `true`. | `nil` | | Header | `string` | Header key used to store the request ID. | "X-Request-ID" | | Generator | `func() string` | Function that generates the identifier. | utils.SecureToken | ## Default Config The default config uses a cryptographically secure token generator for better security and privacy. ```go var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, Generator: utils.SecureToken, } ``` --- ## ResponseTime Response time middleware for [Fiber](https://github.com/gofiber/fiber) that measures the time spent handling a request and exposes it via a response header. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/responsetime" ) ``` ### Default config ```go app.Use(responsetime.New()) ``` ### Custom header ```go app.Use(responsetime.New(responsetime.Config{ Header: "X-Elapsed", })) ``` ### Skip logic ```go app.Use(responsetime.New(responsetime.Config{ Next: func(c fiber.Ctx) bool { return c.Path() == "/healthz" }, })) ``` ## Config | Property | Type | Description | Default | | :------- | :--- | :---------- | :------ | | Next | `func(c fiber.Ctx) bool` | Defines a function to skip this middleware when it returns `true`. | `nil` | | Header | `string` | Header key used to store the measured response time. If left empty, the default header is used. | `"X-Response-Time"` | --- ## Rewrite The Rewrite middleware remaps the request path using custom rules, helping with backward compatibility and cleaner URLs. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Config | Property | Type | Description | Default | |:---------|:----------------------|:------------------------------------------------------|:-----------| | Next | `func(fiber.Ctx) bool` | Skip when function returns `true`. | `nil` | | Rules | `map[string]string` | Map paths to new values; use `$1`, `$2` for wildcard captures.| (Required) | :::note Rules are stored in a map, so iteration order is undefined. Avoid overlapping patterns if precedence matters. ::: ### Examples ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/rewrite" ) func main() { app := fiber.New() app.Use(rewrite.New(rewrite.Config{ Rules: map[string]string{ "/old": "/new", "/old/*": "/new/$1", }, })) app.Get("/new", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Get("/new/*", func(c fiber.Ctx) error { return c.SendString("Wildcard: " + c.Params("*")) }) app.Listen(":3000") } ``` ## Test ```bash curl http://localhost:3000/old curl http://localhost:3000/old/hello ``` --- ## Session The Session middleware adds session management to Fiber apps through the [Storage](https://github.com/gofiber/storage) package, which offers a unified interface for multiple databases. By default, sessions live in memory, but you can plug in any storage backend. ## Table of Contents - [Quick Start](#quick-start) - [Usage Patterns](#usage-patterns) - [Session Security](#session-security) - [Session ID Extractors](#session-id-extractors) - [Configuration](#configuration) - [Migration Guide](#migration-guide) - [API Reference](#api-reference) - [Examples](#examples) ## Quick Start ```go import ( "fmt" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/session" ) // Basic usage app.Use(session.New()) app.Get("/", func(c fiber.Ctx) error { sess := session.FromContext(c) // Get and update visits count var visits int if v := sess.Get("visits"); v != nil { // Use type assertion with an ok check to prevent a panic if vInt, ok := v.(int); ok { visits = vInt } } visits++ sess.Set("visits", visits) return c.SendString(fmt.Sprintf("Visits: %d", visits)) }) ``` ### Production Configuration ```go import ( "time" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/storage/redis/v3" ) storage := redis.New(redis.Config{ Host: "localhost", Port: 6379, }) app.Use(session.New(session.Config{ Storage: storage, CookieSecure: true, // HTTPS only CookieHTTPOnly: true, // Prevent XSS CookieSameSite: "Lax", // CSRF protection IdleTimeout: 30 * time.Minute, // Session timeout AbsoluteTimeout: 24 * time.Hour, // Maximum session life Extractor: extractors.FromCookie("__Host-session_id"), })) Notes: - AbsoluteTimeout must be greater than or equal to IdleTimeout; otherwise, the middleware panics during configuration. - If CookieSameSite is set to "None", the middleware automatically forces CookieSecure=true when setting the cookie. ``` ## Usage Patterns ### Middleware Pattern (Recommended) This pattern automatically manages the session lifecycle and is recommended for most applications. ```go // Setup middleware app.Use(session.New()) // Use in handlers app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) // Session is automatically saved when handler returns sess.Set("user_id", 123) sess.Set("authenticated", true) return c.Redirect("/dashboard") }) ``` **Benefits:** - Automatic session saving - Automatic resource cleanup - No manual lifecycle management - Thread-safe operations ### Store Pattern (Advanced) Use the store pattern for background tasks or when you need direct access to sessions. ```go import ( "context" "log" "time" ) store := session.NewStore() // In background tasks func backgroundTask(sessionID string) { sess, err := store.GetByID(context.Background(), sessionID) if err != nil { return } defer sess.Release() // Important: Manual cleanup required // Modify session sess.Set("last_task", time.Now()) // Manual save required if err := sess.Save(); err != nil { log.Printf("Failed to save session: %v", err) } } ``` **Requirements:** - Must call `sess.Release()` when done - Must call `sess.Save()` to persist changes - Handle errors manually ## Session Security ### Authentication Flow Understanding session lifecycle during authentication is crucial for security. #### Basic Login/Logout ```go app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) email := c.FormValue("email") password := c.FormValue("password") // Simple credential validation (use proper authentication in production) if email == "admin@example.com" && password == "secret" { // Important: Regenerate the session ID to prevent fixation // This changes the session ID while preserving existing data if err := sess.Regenerate(); err != nil { return c.Status(500).SendString("Session error") } // Add authentication data to existing session sess.Set("user_id", 1) sess.Set("authenticated", true) return c.Redirect("/dashboard") } return c.Status(401).SendString("Invalid credentials") }) app.Post("/logout", func(c fiber.Ctx) error { sess := session.FromContext(c) // Complete session reset (clears all data + new session ID) if err := sess.Reset(); err != nil { return c.Status(500).SendString("Session error") } return c.Redirect("/") }) ``` #### Cart Preservation During Login ```go app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) // Validate credentials (implement your own validation) email := c.FormValue("email") password := c.FormValue("password") if !isValidUser(email, password) { return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"}) } // Important: Regenerate the session ID to prevent fixation // This changes the session ID while preserving existing data if err := sess.Regenerate(); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Session error"}) } // Add authentication data to existing session sess.Set("user_id", getUserID(email)) sess.Set("authenticated", true) sess.Set("login_time", time.Now()) return c.JSON(fiber.Map{"status": "logged in"}) }) ``` ### Security Methods Comparison | Method | Session ID | Session Data | Use Case | |--------|------------|--------------|----------| | `Regenerate()` | ✅ Changes | ✅ Preserved | Login, privilege escalation | | `Reset()` | ✅ Changes | ❌ Cleared | Logout, security breach | | `Destroy()` | ⚪ Unchanged | ❌ Cleared | Clear data only | ### Common Security Mistakes ❌ **Session Fixation Vulnerability:** ```go // DANGEROUS: Keeping same session ID after login app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) // Validate user... sess.Set("user_id", userID) // Attacker can hijack this session! return c.Redirect("/dashboard") }) ``` ✅ **Secure Implementation:** ```go // SECURE: Always regenerate session ID after authentication app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) // Validate user... if err := sess.Regenerate(); err != nil { // Prevents session fixation return err } sess.Set("user_id", userID) return c.Redirect("/dashboard") }) ``` ### Authentication Middleware This is a basic example of an authentication middleware that checks if a user is logged in before accessing protected routes. ```go // Authentication check middleware func RequireAuth(c fiber.Ctx) error { sess := session.FromContext(c) if sess == nil { return c.Redirect("/login") } // Check if user is authenticated if sess.Get("authenticated") != true { return c.Redirect("/login") } return c.Next() } // Usage app.Use("/dashboard", RequireAuth) app.Use("/admin", RequireAuth) ``` ### Automatic Session Expiration Sessions automatically expire based on your configuration: ```go app.Use(session.New(session.Config{ IdleTimeout: 30 * time.Minute, // Auto-expire after 30 min of inactivity AbsoluteTimeout: 24 * time.Hour, // Force expire after 24 hours regardless of activity })) ``` **How it works:** - `IdleTimeout`: Storage automatically removes sessions after inactivity period - Any route that uses the middleware will reset the idle timer - Calling `sess.Save()` will also reset the idle timer - `AbsoluteTimeout`: Sessions are forcibly expired after maximum duration - No manual cleanup required - the storage layer handles this ## Session ID Extractors This middleware uses the shared extractors module for session ID extraction. See the [Extractors Guide](../guide/extractors) for more details. ### Built-in Extractors ```go // Cookie-based (recommended for web apps) extractors.FromCookie("session_id") // Header-based (recommended for APIs) extractors.FromHeader("X-Session-ID") // Authorization header (read-only) extractors.FromAuthHeader("Bearer") // Form data extractors.FromForm("session_id") // URL query parameter extractors.FromQuery("session_id") // URL path parameter extractors.FromParam("id") ``` **Session Response Behavior:** - Cookie extractors: set cookie in the response - Header extractors (non-Authorization): set header in the response - Authorization header, Query, Form, Param, Custom: read-only (no response values are set) ### Multiple Sources with Fallback ```go app.Use(session.New(session.Config{ Extractor: extractors.Chain( extractors.FromCookie("session_id"), // Try cookie first extractors.FromHeader("X-Session-ID"), // Then header extractors.FromQuery("session_id"), // Finally query ), })) ``` **Response Behavior with Chained Extractors:** Only cookie and non-Authorization header extractors contribute to response setting. Others are read-only. - Cookie + Header (non-Auth) extractors: both cookie and header are set - Only Cookie extractors: only cookie is set - Only Header (non-Auth) extractors: only header is set - Any mix that includes Authorization/Query/Form/Param/Custom: those sources are read-only ```go // This will set both cookie and header in response extractors.Chain( extractors.FromCookie("session_id"), extractors.FromHeader("X-Session-ID") ) // This will set only cookie in response extractors.Chain( extractors.FromCookie("session_id"), extractors.FromQuery("session_id") // Ignored for response ) // This will set nothing in response (read-only mode) extractors.Chain( extractors.FromQuery("session_id"), extractors.FromForm("session_id") ) ``` ### Custom Extractors (Session-specific) Prefer the helper constructors from the extractors module. See the Extractors Guide for the full API; below are session-specific examples and notes. ```go // Authorization Bearer tokens (read-only for sessions) // The session middleware will NOT set Authorization back in the response. app.Use(session.New(session.Config{ Extractor: extractors.FromAuthHeader("Bearer"), })) ``` ```go // Custom read-only header via FromCustom (read-only for sessions) app.Use(session.New(session.Config{ Extractor: extractors.FromCustom("X-Custom-Session", func(c fiber.Ctx) (string, error) { v := c.Get("X-Custom-Session") if v == "" { return "", extractors.ErrNotFound } return v, nil }), })) ``` ## Configuration ### Storage Options ```go import ( "github.com/gofiber/storage/redis/v3" "github.com/gofiber/storage/postgres/v3" ) // Redis (recommended for production) redisStorage := redis.New(redis.Config{ Host: "localhost", Port: 6379, Password: "", Database: 0, }) // PostgreSQL pgStorage := postgres.New(postgres.Config{ Host: "localhost", Port: 5432, Database: "sessions", Username: "user", Password: "pass", }) app.Use(session.New(session.Config{ Storage: redisStorage, })) ``` ### Production Security Settings ```go import ( "log" "time" "github.com/gofiber/utils/v2" "github.com/gofiber/fiber/v3/extractors" ) app.Use(session.New(session.Config{ // Storage Storage: redisStorage, // Security CookieSecure: true, // HTTPS only (required in production) CookieHTTPOnly: true, // No JavaScript access (prevents XSS) CookieSameSite: "Lax", // CSRF protection // Session Management IdleTimeout: 30 * time.Minute, // Inactivity timeout AbsoluteTimeout: 24 * time.Hour, // Maximum session duration // Cookie Settings CookiePath: "/", CookieDomain: "example.com", CookieSessionOnly: false, // Persist across browser restarts // Session ID Extractor: extractors.FromCookie("__Host-session_id"), KeyGenerator: utils.SecureToken, // Error Handling ErrorHandler: func(c fiber.Ctx, err error) { log.Printf("Session error: %v", err) }, })) ``` ### Custom Types Session data supports basic Go types by default: - `string`, `int`, `int8`, `int16`, `int32`, `int64` - `uint`, `uint8`, `uint16`, `uint32`, `uint64` - `bool`, `float32`, `float64` - `[]byte`, `complex64`, `complex128` - `any` For custom types (structs, maps, slices), you must register them for encoding/decoding: ```go import "fmt" type User struct { ID int `json:"id"` Name string `json:"name"` Role string `json:"role"` } // Method 1: Using NewWithStore func main() { app := fiber.New() sessionMiddleware, store := session.NewWithStore() store.RegisterType(User{}) // Register custom type app.Use(sessionMiddleware) app.Get("/", func(c fiber.Ctx) error { sess := session.FromContext(c) // Use custom type sess.Set("user", User{ID: 123, Name: "John", Role: "admin"}) user, ok := sess.Get("user").(User) if ok { return c.JSON(fiber.Map{"user": user.Name, "role": user.Role}) } return c.SendString("No user found") }) app.Listen(":3000") } ``` ```go // Method 2: Using separate store store := session.NewStore() store.RegisterType(User{}) app.Use(session.New(session.Config{ Store: store, })) // Usage in handlers sess.Set("user", User{ID: 123, Name: "John", Role: "admin"}) user, ok := sess.Get("user").(User) if ok { fmt.Printf("User: %s (Role: %s)", user.Name, user.Role) } ``` **Important Notes:** - Custom types must be registered before using them in sessions - Registration must happen during application startup - All instances of the application must register the same types - Types are encoded using Go's `gob` package ## Migration Guide ### v2 to v3 Breaking Changes 1. **Function Signature**: `session.New()` now returns middleware handler, not store 2. **Session ID Extraction**: `KeyLookup` replaced with `Extractor` functions 3. **Lifecycle Management**: Manual `Release()` required for store pattern 4. **Timeout Handling**: `Expiration` split into `IdleTimeout` and `AbsoluteTimeout` ### Migration Examples **v2 Code:** ```go store := session.New(session.Config{ KeyLookup: "cookie:session_id", }) app.Get("/", func(c fiber.Ctx) error { sess, err := store.Get(c) if err != nil { return err } // Session automatically saved and released sess.Set("key", "value") return nil }) ``` **v3 Middleware Pattern (Recommended):** ```go app.Use(session.New(session.Config{ Extractor: extractors.FromCookie("session_id"), })) app.Get("/", func(c fiber.Ctx) error { sess := session.FromContext(c) // Session automatically saved and released sess.Set("key", "value") return nil }) ``` **v3 Store Pattern (Advanced):** ```go store := session.NewStore(session.Config{ Extractor: extractors.FromCookie("session_id"), }) app.Get("/", func(c fiber.Ctx) error { sess, err := store.Get(c) if err != nil { return err } defer sess.Release() // Manual cleanup required sess.Set("key", "value") return sess.Save() // Manual save required }) ``` ### KeyLookup to Extractor Migration | v2 KeyLookup | v3 Extractor | |---------------------------------|------------------------------------------------------------------------------------| | `"cookie:session_id"` | `extractors.FromCookie("session_id")` | | `"header:X-Session-ID"` | `extractors.FromHeader("X-Session-ID")` | | `"query:session_id"` | `extractors.FromQuery("session_id")` | | `"form:session_id"` | `extractors.FromForm("session_id")` | | `"cookie:sid,header:X-Sid"` | `extractors.Chain(extractors.FromCookie("sid"), extractors.FromHeader("X-Sid"))` | ## API Reference ### Middleware Methods (Recommended) ```go sess := session.FromContext(c) // Data operations sess.Get(key any) any sess.Set(key, value any) sess.Delete(key any) sess.Keys() []any // Session management sess.ID() string sess.Fresh() bool sess.Regenerate() error // Change ID, keep data sess.Reset() error // Change ID, clear data sess.Destroy() error // Keep ID, clear data // Context-aware variants propagate cancellation/deadlines to storage I/O. // Pass a context.Context to control timeouts; a nil context is treated as // context.Background(). sess.RegenerateWithContext(ctx context.Context) error sess.ResetWithContext(ctx context.Context) error sess.DestroyWithContext(ctx context.Context) error // Store access sess.Store() *session.Store ``` `FromContext` accepts a `fiber.CustomCtx`, `fiber.Ctx`, a `*fasthttp.RequestCtx`, or a `context.Context`. ### Store Methods ```go store := session.NewStore() // Store operations store.Get(c fiber.Ctx) (*session.Session, error) store.GetByID(ctx context.Context, sessionID string) (*session.Session, error) store.Reset(ctx context.Context) error store.Delete(ctx context.Context, sessionID string) error // Type registration store.RegisterType(User{}) ``` ### Session Methods (Store Pattern) ```go sess, err := store.Get(c) defer sess.Release() // Required! // Same methods as middleware, plus: sess.Save() error // Manual save required sess.SetIdleTimeout(duration) // Per-session timeout sess.Release() // Manual cleanup required // Context-aware variants propagate cancellation/deadlines to storage I/O. // A nil context is treated as context.Background(). sess.DestroyWithContext(ctx context.Context) error sess.RegenerateWithContext(ctx context.Context) error sess.ResetWithContext(ctx context.Context) error sess.SaveWithContext(ctx context.Context) error ``` ### Session with Context (timeouts/cancellation) The `*WithContext` variants let you propagate a `context.Context` to the underlying storage call so that slow or unresponsive backends can be bounded by a deadline or cancelled. This mirrors the `Storage` and `SharedState` `WithContext` convention. ```go ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() // Persist the session, bounding the storage write by the deadline. if err := sess.SaveWithContext(ctx); err != nil { // deadline exceeded, cancellation, or storage error } // Destroy is also covered: // if err := sess.DestroyWithContext(ctx); err != nil { ... } ``` A `nil` context is treated as `context.Background()`, so `sess.SaveWithContext(nil)` is equivalent to `sess.Save()` when no request context is available. ### Extractor Functions ```go // Built-in extractors (import "github.com/gofiber/fiber/v3/extractors") extractors.FromCookie(key string) extractors.Extractor extractors.FromHeader(key string) extractors.Extractor extractors.FromQuery(key string) extractors.Extractor extractors.FromForm(key string) extractors.Extractor extractors.FromParam(key string) extractors.Extractor // Chaining extractors.Chain(extractors ...extractors.Extractor) extractors.Extractor ``` ### Config Properties | Property | Type | Description | Default | |---------------------|-----------------------------|-----------------------------|--------------------------------------------| | `Store` | `*session.Store` | Pre-built session store (use when you need to share/register types) | `nil` (auto-created) | | `Storage` | `fiber.Storage` | Session storage backend (used when creating a store if `Store` is nil) | `memory.New()` | | `Extractor` | `extractors.Extractor` | Session ID extraction | `extractors.FromCookie("session_id")` | | `KeyGenerator` | `func() string` | Session ID generator | `utils.SecureToken` | | `IdleTimeout` | `time.Duration` | Inactivity timeout | `30 * time.Minute` | | `AbsoluteTimeout` | `time.Duration` | Maximum session duration | `0` (unlimited) | | `CookieSecure` | `bool` | HTTPS only | `false` | | `CookieHTTPOnly` | `bool` | No JavaScript access | `false` | | `CookieSameSite` | `string` | SameSite attribute | `"Lax"` | | `CookiePath` | `string` | Cookie path | `""` | | `CookieDomain` | `string` | Cookie domain | `""` | | `CookieSessionOnly` | `bool` | Session cookie | `false` | | `Next` | `func(fiber.Ctx) bool` | Skip middleware when returns true | `nil` | | `ErrorHandler` | `func(fiber.Ctx, error)` | Error callback | `DefaultErrorHandler` | ## Examples ### E-commerce with Cart Persistence ```go import ( "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/session" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/storage/redis/v3" ) func main() { app := fiber.New() // Session middleware app.Use(session.New(session.Config{ Storage: redis.New(), CookieSecure: true, CookieHTTPOnly: true, CookieSameSite: "Lax", IdleTimeout: 30 * time.Minute, AbsoluteTimeout: 24 * time.Hour, Extractor: extractors.FromCookie("__Host-cart_session"), })) // Add to cart (anonymous user) app.Post("/cart/add", func(c fiber.Ctx) error { sess := session.FromContext(c) cart, _ := sess.Get("cart").([]string) cart = append(cart, c.FormValue("item_id")) sess.Set("cart", cart) return c.JSON(fiber.Map{"items": len(cart)}) }) // Login (preserve session data) app.Post("/login", func(c fiber.Ctx) error { sess := session.FromContext(c) // Simple validation (implement proper authentication) email := c.FormValue("email") password := c.FormValue("password") if email != "user@example.com" || password != "password" { return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"}) } // Regenerate session ID for security // This changes the session ID while preserving existing data if err := sess.Regenerate(); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Session error"}) } sess.Set("user_id", 1) sess.Set("authenticated", true) return c.JSON(fiber.Map{"status": "logged in"}) }) // Logout (clear everything) app.Post("/logout", func(c fiber.Ctx) error { sess := session.FromContext(c) // Reset clears all data and generates new session ID if err := sess.Reset(); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Session error"}) } return c.JSON(fiber.Map{"status": "logged out"}) }) app.Listen(":3000") } // Helper functions (implement these properly in production) func isValidUser(email, password string) bool { return email == "user@example.com" && password == "password" } func getUserID(email string) int { return 1 // Return actual user ID from database } ``` ### API with Header-based Sessions ```go import ( "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/session" "github.com/gofiber/fiber/v3/extractors" "github.com/gofiber/storage/redis/v3" ) func main() { app := fiber.New() // API session middleware with header extraction app.Use(session.New(session.Config{ Storage: redis.New(), Extractor: extractors.FromHeader("X-Session-Token"), IdleTimeout: time.Hour, })) // API endpoint app.Post("/api/data", func(c fiber.Ctx) error { sess := session.FromContext(c) // Track API usage count, _ := sess.Get("api_calls").(int) count++ sess.Set("api_calls", count) sess.Set("last_call", time.Now()) return c.JSON(fiber.Map{ "data": "some data", "calls": count, }) }) app.Listen(":3000") } ``` ### Multi-source Session ID Support ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/session" "github.com/gofiber/fiber/v3/extractors" ) func main() { app := fiber.New() // Support multiple sources with priority app.Use(session.New(session.Config{ Extractor: extractors.Chain( extractors.FromCookie("session_id"), // 1st: Cookie (web) extractors.FromHeader("X-Session-ID"), // 2nd: Header (API) extractors.FromQuery("session_id"), // 3rd: Query (fallback) ), })) app.Get("/", func(c fiber.Ctx) error { sess := session.FromContext(c) // Works with any of the above methods return c.JSON(fiber.Map{ "session_id": sess.ID(), "source": "multi-source", }) }) app.Listen(":3000") } ``` --- ## Skip The Skip middleware wraps a handler and bypasses it when the predicate returns `true` for the current request. ## Signatures ```go func New(handler fiber.Handler, exclude func(c fiber.Ctx) bool) fiber.Handler ``` ## Examples Import the package: ```go import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/skip" ) ``` `skip.New` accepts the handler to wrap and a predicate function. The predicate runs for every request, and returning `true` skips the wrapped handler and executes the next middleware in the chain. After you initialize your Fiber app, use `skip.New` like this: ```go func main() { app := fiber.New() app.Use(skip.New(BasicHandler, func(ctx fiber.Ctx) bool { return ctx.Method() == fiber.MethodGet })) app.Get("/", func(ctx fiber.Ctx) error { return ctx.SendString("It was a GET request!") }) log.Fatal(app.Listen(":3000")) } func BasicHandler(ctx fiber.Ctx) error { return ctx.SendString("It was not a GET request!") } ``` :::tip `app.Use` processes requests on any route and method. In the example above, the handler is skipped only for `GET`. ::: --- ## SSE The SSE handler provides the transport pieces for Server-Sent Events: response headers, event formatting, flushing, heartbeat comments, and disconnect detection through `Flush` errors. It intentionally does not include a hub, topics, authentication, replay storage, metrics, or external pub/sub bridges. Those are application concerns that can be composed around the stream handler. ## Signatures ```go func New(config ...Config) fiber.Handler ``` ## Examples Import the SSE package: ```go import ( "context" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/sse" ) ``` Once your Fiber app is initialized, mount an SSE endpoint like this: ```go app.Get("/events", sse.New(sse.Config{ Retry: 5 * time.Second, Handler: func(c fiber.Ctx, stream *sse.Stream) error { return stream.Event(sse.Event{ Name: "message", Data: fiber.Map{"message": "hello"}, }) }, })) ``` For long-running streams, subscribe each client to its own event channel and stop when the client disconnects. A single shared channel load-balances messages across clients; use a fan-out source when every client must receive every event: ```go type Broker interface { Subscribe(ctx context.Context) (<-chan string, error) } app.Get("/events", sse.New(sse.Config{ Handler: func(c fiber.Ctx, stream *sse.Stream) error { events, err := broker.Subscribe(stream.Context()) if err != nil { return err } for { select { case msg, ok := <-events: if !ok { return nil } if err := stream.Event(sse.Event{Name: "message", Data: msg}); err != nil { return err } case <-stream.Done(): return stream.Err() } } }, })) ``` `stream.Context()` is canceled when the stream ends or a write fails, which makes it convenient to pass into database, broker, or gRPC calls: ```go app.Get("/events", sse.New(sse.Config{ Handler: func(c fiber.Ctx, stream *sse.Stream) error { rows, err := db.QueryContext(stream.Context(), "SELECT id FROM jobs") if err != nil { return err } defer rows.Close() return stream.Comment("connected") }, })) ``` ## Config | Property | Type | Description | Default | |:------------------|:-------------------------|:-----------------------------------------------------------------------------------------------------|:----------------------| | Handler | `sse.Handler` | Required. Writes events to the stream. `New` panics if this field is omitted or `nil`. | required (`nil` panics) | | OnClose | `func(fiber.Ctx, error)` | Called when the stream ends, with `nil` when the handler returned successfully and no stream write failed. | `nil` | | Retry | `time.Duration` | Initial EventSource reconnect delay. | `0` | | HeartbeatInterval | `time.Duration` | Interval for SSE comment heartbeats. | `15 * time.Second` | | DisableHeartbeat | `bool` | Disable automatic heartbeat comments. When disabled, disconnected clients may not be detected until the next write. | `false` | ## Default Config ```go var ConfigDefault = Config{ Handler: nil, OnClose: nil, Retry: 0, HeartbeatInterval: 15 * time.Second, DisableHeartbeat: false, } ``` `ConfigDefault.Handler` is `nil`, but `Handler` is still required. Set it before calling `New`, or `New` will panic. ## Stream ```go func (s *Stream) Event(event Event) error func (s *Stream) Comment(comment string) error func (s *Stream) Retry(retry time.Duration) error func (s *Stream) Context() context.Context func (s *Stream) Done() <-chan struct{} func (s *Stream) Err() error func (s *Stream) LastEventID() string ``` Every write is flushed. A failed flush closes `Done`, stores the error returned by `Err`, and lets the handler stop without relying on `fasthttp.RequestCtx.Done`, which is not a per-client disconnect signal. After a normal handler return, `Done` is closed and `Context()` is canceled while `Err()` remains `nil`; writes after that return `sse: stream closed`. Automatic heartbeat comments keep idle streams active and make silent client disconnects observable through the next flush error. If heartbeats are disabled, a handler waiting on an external source might not notice a disconnected client until it writes again. Stopping a stream waits for an in-flight heartbeat write to finish, so a very slow client can delay shutdown until the underlying write unblocks. `Config.Retry` sends the initial reconnect delay when the stream opens. `Event.Retry` changes the reconnect delay for a specific event, following the SSE wire format. --- ## Static The Static middleware serves assets such as **images**, **CSS**, and **JavaScript**. :::info By default, it serves `index.html` when a directory is requested. Customize this behavior in the [Config](#config) options. ::: ## Signatures ```go func New(root string, cfg ...Config) fiber.Handler ``` ## Examples Import the package: ```go import( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/static" ) ``` ### Serving files from a directory ```go app.Get("/*", static.New("./public")) ```
Test ```sh curl http://localhost:3000/hello.html curl http://localhost:3000/css/style.css ```
### Serving files from a directory with `Use` ```go app.Use("/", static.New("./public")) ```
Test ```sh curl http://localhost:3000/hello.html curl http://localhost:3000/css/style.css ```
### Serving a single file ```go app.Use("/static", static.New("./public/hello.html")) ```
Test ```sh curl http://localhost:3000/static # will show hello.html curl http://localhost:3000/static/john/doe # will show hello.html ```
### Serving files using os.DirFS ```go app.Get("/files*", static.New("", static.Config{ FS: os.DirFS("files"), Browse: true, })) ```
Test ```sh curl http://localhost:3000/files/css/style.css curl http://localhost:3000/files/index.html ```
### Serving files using embed.FS ```go //go:embed path/to/files var myfiles embed.FS app.Get("/files*", static.New("", static.Config{ FS: myfiles, Browse: true, })) ```
Test ```sh curl http://localhost:3000/files/css/style.css curl http://localhost:3000/files/index.html ```
### SPA (Single Page Application) ```go app.Use("/web", static.New("", static.Config{ FS: os.DirFS("dist"), })) app.Get("/web*", func(c fiber.Ctx) error { return c.SendFile("dist/index.html") }) ```
Test ```sh curl http://localhost:3000/web/css/style.css curl http://localhost:3000/web/index.html curl http://localhost:3000/web ```
:::caution To define static routes using `Get`, append the wildcard (`*`) operator at the end of the route. ::: ## Config | Property | Type | Description | Default | |:-----------|:------------------------|:---------------------------------------------------------------------------------------------------------------------------|:-----------------------| | Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when it returns true. | `nil` | | FS | `fs.FS` | FS is the file system to serve the static files from.You can use interfaces compatible with fs.FS like embed.FS, os.DirFS etc. | `nil` | | Compress | `bool` | When set to true, the server tries minimizing CPU usage by caching compressed files. The middleware will compress the response using `gzip`, `brotli`, or `zstd` compression depending on the [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. This works differently than the github.com/gofiber/compression middleware. | `false` | | ByteRange | `bool` | When set to true, enables byte range requests. | `false` | | Browse | `bool` | When set to true, enables directory browsing. | `false` | | Download | `bool` | When set to true, enables direct download. | `false` | | IndexNames | `[]string` | The names of the index files for serving a directory. | `[]string{"index.html"}` | | CacheDuration | `time.Duration` | Expiration duration for inactive file handlers.Use a negative time.Duration to disable it. | `10 * time.Second` | | MaxAge | `int` | The value for the Cache-Control HTTP-header that is set on the file response. MaxAge is defined in seconds. | `0` | | ModifyResponse | `fiber.Handler` | ModifyResponse defines a function that allows you to alter the response. | `nil` | | NotFoundHandler | `fiber.Handler` | NotFoundHandler defines a function to handle when the path is not found. | `nil` | When **Download** is enabled, the response includes a `Content-Disposition` header with the requested filename. Non-ASCII names use the `filename*` parameter as defined by [RFC 6266](https://www.rfc-editor.org/rfc/rfc6266) and [RFC 8187](https://www.rfc-editor.org/rfc/rfc8187). :::info You can set `CacheDuration` config property to `-1` to disable caching. ::: ## Default Config ```go var ConfigDefault = Config{ IndexNames: []string{"index.html"}, CacheDuration: 10 * time.Second, } ``` --- ## Timeout The timeout middleware enforces a deadline on handler execution. It wraps handlers with `context.WithTimeout`, exposes the derived context through `c.Context()`, and returns `408 Request Timeout` when the deadline is exceeded. ## How It Works When a timeout occurs, the middleware **returns immediately** without waiting for the handler to finish. This is achieved through Fiber's **Abandon mechanism**: 1. The handler runs in a goroutine with a timeout context 2. On timeout, the middleware marks the context as "abandoned" and returns `408` immediately 3. The handler goroutine can continue safely (e.g., for cleanup) without blocking the response 4. A background cleanup goroutine waits for the handler to finish and performs context cleanup Handlers can detect the timeout by listening on `c.Context().Done()` and return early. This is the recommended pattern for cooperative cancellation. If a handler panics, the middleware catches it and returns `500 Internal Server Error`. ## Known limitations - Timed-out requests abandon their `fiber.Ctx` to avoid data races with the core request handler (including the `ErrorHandler`). These contexts are **not** returned to the pool, so each timed-out request leaks a context. Calling `ForceRelease` is only safe if you can guarantee that no goroutine (including Fiber internals) will touch the context anymore; the timeout middleware intentionally does not call it. :::caution `timeout.New` wraps your final handler and can't be added with `app.Use` or used in a middleware chain. Register it per route and avoid calling `c.Next()` inside the wrapped handler—doing so will panic. ::: ## Signatures ```go func New(handler fiber.Handler, config ...timeout.Config) fiber.Handler ``` ## Examples ### Basic example The following program times out any request that takes longer than two seconds. The handler simulates work with `sleepWithContext`, which stops when the context is canceled: ```go package main import ( "context" "fmt" "log" "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/timeout" ) func sleepWithContext(ctx context.Context, d time.Duration) error { select { case <-time.After(d): return nil case <-ctx.Done(): return ctx.Err() } } func main() { app := fiber.New() handler := func(c fiber.Ctx) error { delay, _ := time.ParseDuration(c.Params("delay") + "ms") if err := sleepWithContext(c.Context(), delay); err != nil { return fmt.Errorf("%w: execution error", err) } return c.SendString("finished") } app.Get("/sleep/:delay", timeout.New(handler, timeout.Config{ Timeout: 2 * time.Second, })) log.Fatal(app.Listen(":3000")) } ``` Use these requests to see the middleware in action: ```bash curl -i http://localhost:3000/sleep/1000 # finishes within the timeout curl -i http://localhost:3000/sleep/3000 # returns 408 Request Timeout ``` ## Config | Property | Type | Description | Default | |:------------|:-------------------|:---------------------------------------------------------------------|:-------| | Next | `func(fiber.Ctx) bool` | Function to skip this middleware when it returns `true`. | `nil` | | Timeout | `time.Duration` | Timeout duration for requests. `0` or a negative value disables the timeout. | `0` | | OnTimeout | `fiber.Handler` | Handler executed when a timeout occurs. Defaults to returning `fiber.ErrRequestTimeout`. | `nil` | | Errors | `[]error` | Custom errors treated as timeout errors. | `nil` | ### Use with a custom error ```go var ErrFooTimeOut = errors.New("foo context canceled") func main() { app := fiber.New() h := func(c fiber.Ctx) error { sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") if err := sleepWithContextWithCustomError(c.Context(), sleepTime); err != nil { return fmt.Errorf("%w: execution error", err) } return nil } app.Get("/foo/:sleepTime", timeout.New(h, timeout.Config{Timeout: 2 * time.Second, Errors: []error{ErrFooTimeOut}})) log.Fatal(app.Listen(":3000")) } func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { timer := time.NewTimer(d) select { case <-ctx.Done(): if !timer.Stop() { <-timer.C } return ErrFooTimeOut case <-timer.C: } return nil } ``` ### Sample usage with a database call ```go func main() { app := fiber.New() db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{}) handler := func(ctx fiber.Ctx) error { tran := db.WithContext(ctx.Context()).Begin() if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil { return tran.Error } if tran = tran.Commit(); tran.Error != nil { return tran.Error } return nil } app.Get("/foo", timeout.New(handler, timeout.Config{Timeout: 10 * time.Second})) log.Fatal(app.Listen(":3000")) } ``` --- ## 🆕 What's New in v3 ## 🎉 Welcome We are excited to announce the release of Fiber v3! 🚀 In this guide, we'll walk you through the most important changes in Fiber `v3` and show you how to migrate your existing Fiber `v2` applications to Fiber `v3`. ### 🛠️ Migration tool Fiber v3 introduces a CLI-powered migration helper. Install the CLI and let it update your project automatically: ```bash go install github.com/gofiber/cli/fiber@latest fiber migrate --to v3 ``` See the [migration guide](#-migration-guide) for more details and options. Here's a quick overview of the changes in Fiber `v3`: - [🚀 App](#-app) - [🎣 Hooks](#-hooks) - [🚀 Listen](#-listen) - [🗺️ Router](#-router) - [🧠 Context](#-context) - [📎 Binding](#-binding) - [🔬 Extractors Package](#-extractors-package) - [🔄️ Redirect](#-redirect) - [🌎 Client package](#-client-package) - [🧰 Generic functions](#-generic-functions) - [🛠️ Utils](#utils) - [🧩 Services](#-services) - [📃 Log](#-log) - [📦 Storage Interface](#-storage-interface) - [🧬 Middlewares](#-middlewares) - [Important Change for Accessing Middleware Data](#important-change-for-accessing-middleware-data) - [Adaptor](#adaptor) - [BasicAuth](#basicauth) - [Cache](#cache) - [CORS](#cors) - [CSRF](#csrf) - [Compression](#compression) - [EncryptCookie](#encryptcookie) - [Favicon](#favicon) - [Filesystem](#filesystem) - [Healthcheck](#healthcheck) - [KeyAuth](#keyauth) - [Logger](#logger) - [Monitor](#monitor) - [Proxy](#proxy) - [Recover](#recover) - [Session](#session) - [SSE](#sse) - [🔌 Addons](#-addons) - [📋 Migration guide](#-migration-guide) ## Dropping support for old Go versions Fiber `v3` requires Go `1.25` or later. Update your toolchain to `1.25+` before upgrading so the module `go` directive and standard library features align with the new minimum version. ## 🚀 App We have made several changes to the Fiber app, including: - **Listen**: The `Listen` method has been unified with the configuration, allowing for more streamlined setup. - **Static**: The `Static` method has been removed and its functionality has been moved to the [static middleware](./middleware/static.md). - **app.Config properties**: Several properties have been moved to the listen configuration: - `DisableStartupMessage` - `EnablePrefork` (previously `Prefork`) - `EnablePrintRoutes` - `ListenerNetwork` (previously `Network`) - **Trusted Proxy Configuration**: The `EnabledTrustedProxyCheck` has been moved to `app.Config.TrustProxy`, and `TrustedProxies` has been moved to `TrustProxyConfig.Proxies`. Additionally, `ProxyHeader` must be set to read client IPs from proxy headers (e.g., `X-Forwarded-For`). - **XMLDecoder Config Property**: The `XMLDecoder` property has been added to allow usage of 3rd-party XML libraries in XML binder. - **SkipUnmatchedRoutes Config Property**: Opt-in flag that answers requests with no matching route with `404`/`405` before the middleware chain runs. Note that middleware (loggers, static or catch-all responders) does not run for these requests; CORS preflight requests are exempt so cors middleware keeps working. Customize the responses via `ErrorHandler`. ### New Methods - **RegisterCustomBinder**: Allows for the registration of custom binders. - **RegisterCustomConstraint**: Allows for the registration of custom constraints. - **NewWithCustomCtx**: Initialize an app with a custom context in one step. - **State**: Provides a global state for the application, which can be used to store and retrieve data across the application. Check out the [State](./api/state) method for further details. - **SharedState**: Introduces storage-backed app state for prefork-safe/multi-process coordination via `Config.SharedStorage`, with optional `Config.SharedStatePrefix` namespacing, codec-aware helpers (`SetJSON`, `SetMsgPack`, `SetCBOR`, `SetXML`, matching getters, and `WithContext` variants), empty-key no-op handling, and `Reset`/`Close` passthrough helpers. - **NewErrorf**: Allows variadic parameters when creating formatted errors. - **GetBytes / GetString**: Helpers that detach values only when `Immutable` is enabled and the data still references request or response buffers. Access via `c.App().GetString` and `c.App().GetBytes`. - **ReloadViews**: Lets you re-run the configured view engine's `Load()` logic at runtime, including guard rails for missing or nil view engines so development hot-reload hooks can refresh templates safely. #### Custom Route Constraints Custom route constraints enable you to define your own validation rules for route parameters. Use `RegisterCustomConstraint` to add a constraint type that implements the `CustomConstraint` interface.
Example ```go 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 } app.RegisterCustomConstraint(&UlidConstraint{}) app.Get("/login/:id", func(c fiber.Ctx) error { return c.SendString("User " + c.Params("id")) }) ```
### Removed Methods - **Mount**: Use `app.Use()` instead. - **ListenTLS**: Use `app.Listen()` with `tls.Config`. - **ListenTLSWithCertificate**: Use `app.Listen()` with `tls.Config`. - **ListenMutualTLS**: Use `app.Listen()` with `tls.Config`. - **ListenMutualTLSWithCertificate**: Use `app.Listen()` with `tls.Config`. ### Method Changes - **Test**: The `Test` method has replaced the timeout parameter with a configuration parameter. `0` or lower represents no timeout. - **Listen**: Now has a configuration parameter. - **Listener**: Now has a configuration parameter. ### Custom Ctx Interface in Fiber v3 Fiber v3 introduces a customizable `Ctx` interface, allowing developers to extend and modify the context to fit their needs. This feature provides greater flexibility and control over request handling. #### Idea Behind Custom Ctx Classes The idea behind custom `Ctx` classes is to give developers the ability to extend the default context with additional methods and properties tailored to the specific requirements of their application. This allows for better request handling and easier implementation of specific logic. #### NewWithCustomCtx `NewWithCustomCtx` creates the application and sets the custom context factory at initialization time. ```go title="Signature" func NewWithCustomCtx(fn func(app *App) CustomCtx, config ...Config) *App ```
Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" ) type CustomCtx struct { fiber.DefaultCtx } func (c *CustomCtx) CustomMethod() string { return "custom value" } func main() { app := fiber.NewWithCustomCtx(func(app *fiber.App) fiber.CustomCtx { return &CustomCtx{ DefaultCtx: *fiber.NewDefaultCtx(app), } }) app.Get("/", func(c fiber.Ctx) error { customCtx := c.(*CustomCtx) return c.SendString(customCtx.CustomMethod()) }) log.Fatal(app.Listen(":3000")) } ``` This example creates a `CustomCtx` with an extra `CustomMethod` and initializes the app with `NewWithCustomCtx`.
### Configurable TLS Minimum Version We have added support for configuring the TLS minimum version. This field allows you to set the TLS minimum version for TLSAutoCert and the server listener. ```go app.Listen(":444", fiber.ListenConfig{TLSMinVersion: tls.VersionTLS12}) ``` #### TLS AutoCert support (ACME / Let's Encrypt) We have added native support for automatic certificates management from Let's Encrypt and any other ACME-based providers. ```go // Certificate manager certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, // Replace with your domain name HostPolicy: autocert.HostWhitelist("example.com"), // Folder to store the certificates Cache: autocert.DirCache("./certs"), } app.Listen(":444", fiber.ListenConfig{ AutoCertManager: certManager, }) ``` ### MIME Constants `MIMEApplicationJavaScript` and `MIMEApplicationJavaScriptCharsetUTF8` are deprecated. Use `MIMETextJavaScript` and `MIMETextJavaScriptCharsetUTF8` instead. ## 🎣 Hooks We have made several changes to the Fiber hooks, including: - Added new shutdown hooks to provide better control over the shutdown process: - `OnPreShutdown` - Executes before the server starts shutting down - `OnPostShutdown` - Executes after the server has shut down, receives any shutdown error - `OnPreStartupMessage` - Executes before the startup message is printed, allowing customization of the banner and info entries - `OnPostStartupMessage` - Executes after the startup message is printed, allowing post-startup logic - Deprecated `OnShutdown` in favor of the new pre/post shutdown hooks - Improved shutdown hook execution order and reliability - Added mutex protection for hook registration and execution Important: When using shutdown hooks, ensure app.Listen() is called in a separate goroutine: ```go // Correct usage go app.Listen(":3000") // ... register shutdown hooks app.Shutdown() // Incorrect usage - hooks won't work app.Listen(":3000") // This blocks app.Shutdown() // Never reached ``` ## 🚀 Listen We have made several changes to the Fiber listen, including: - Removed `OnShutdownError` and `OnShutdownSuccess` from `ListenConfig` in favor of using the `OnPostShutdown` hook, which receives the shutdown error ```go app := fiber.New() // Before - using ListenConfig callbacks app.Listen(":3000", fiber.ListenConfig{ OnShutdownError: func(err error) { log.Printf("Shutdown error: %v", err) }, OnShutdownSuccess: func() { log.Println("Shutdown successful") }, }) // After - using OnPostShutdown hook app.Hooks().OnPostShutdown(func(err error) error { if err != nil { log.Printf("Shutdown error: %v", err) } else { log.Println("Shutdown successful") } return nil }) go app.Listen(":3000") ``` This change simplifies the shutdown handling by consolidating the shutdown callbacks into a single hook that receives the error status. - Added support for Unix domain sockets via `ListenerNetwork` and `UnixSocketFileMode` ```go // v2 - Requires manual deletion of old file and permissions change app := fiber.New(fiber.Config{ Network: "unix", }) os.Remove("app.sock") app.Hooks().OnListen(func(fiber.ListenData) error { return os.Chmod("app.sock", 0770) }) app.Listen("app.sock") // v3 - Fiber does it for you app := fiber.New() app.Listen("app.sock", fiber.ListenConfig{ ListenerNetwork: fiber.NetworkUnix, UnixSocketFileMode: 0770, }) ``` - Added `TLSConfig` to `ListenConfig` so external providers can supply certificates via `GetCertificate`. Prefer `TLSConfig` when configuring TLS; when set, it is cloned and takes precedence over other TLS fields. ```go app := fiber.New() app.Listen(":443", fiber.ListenConfig{ TLSConfig: &tls.Config{ GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { return myProvider.Certificate(info.ServerName) }, }, }) ``` - Expanded `ListenData` with versioning, handler, process, and PID metadata, plus dedicated startup message hooks for customization. Check out the [Hooks](./api/hooks#startup-message-customization) documentation for further details. ```go title="Customize the startup message" package main import ( "fmt" "os" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { sm.BannerHeader = "FOOBER " + sm.Version + "\n-------" // Optional: you can also remove old entries // sm.ResetEntries() sm.AddInfo("git-hash", "Git hash", os.Getenv("GIT_HASH")) sm.AddInfo("prefork", "Prefork", fmt.Sprintf("%v", sm.Prefork), 15) return nil }) app.Hooks().OnPostStartupMessage(func(sm *fiber.PostStartupMessageData) error { if !sm.Disabled && !sm.IsChild && !sm.Prevented { fmt.Println("startup completed") } return nil }) app.Listen(":5000") } ``` ## 🗺 Router We have slightly adapted our router interface ### Handler compatibility Fiber now ships with a routing adapter (see `adapter.go`) that understands native Fiber handlers alongside `net/http` and `fasthttp` handlers. Route registration helpers accept a required `handler` argument plus optional additional `handlers`, all typed as `any`, and the adapter transparently converts supported handler styles so you can keep using the ecosystem functions you're familiar with. To align even closer with Express, you can also register handlers that accept the new `fiber.Req` and `fiber.Res` helper interfaces. The adapter understands both two-argument (`func(fiber.Req, fiber.Res)`) and three-argument (`func(fiber.Req, fiber.Res, func() error)`) callbacks, regardless of whether they return an `error`. It also accepts Express-style `next` callbacks that take an `error` (`func(error)` or `func(error) error`). When you include the optional `next` callback, Fiber wires it to `c.Next()` for you so middleware continues to behave as expected. Calling `next(nil)` continues the chain, while passing a non-nil error short-circuits and returns that error. If your handler returns an `error`, the value returned from the injected `next()` bubbles straight back to the caller. When your handler omits an `error` return, Fiber records the result of `next()` and returns it after your function exits so downstream failures still propagate. | Case | Handler signature | Notes | | ---- | ----------------- | ----- | | 1 | `fiber.Handler` | Native Fiber handler. | | 2 | `func(fiber.Ctx)` | Fiber handler without an error return. | | 3 | `func(fiber.Req, fiber.Res) error` | Express-style request handler with error return. | | 4 | `func(fiber.Req, fiber.Res)` | Express-style request handler without error return. | | 5 | `func(fiber.Req, fiber.Res, func() error) error` | Express-style middleware with an error-returning `next` callback and handler error return. | | 6 | `func(fiber.Req, fiber.Res, func() error)` | Express-style middleware with an error-returning `next` callback. | | 7 | `func(fiber.Req, fiber.Res, func()) error` | Express-style middleware with a no-argument `next` callback and handler error return. | | 8 | `func(fiber.Req, fiber.Res, func())` | Express-style middleware with a no-argument `next` callback. | | 9 | `func(fiber.Req, fiber.Res, func(error))` | Express-style middleware with an error-accepting `next` callback. | | 10 | `func(fiber.Req, fiber.Res, func(error)) error` | Express-style middleware with an error-accepting `next` callback and handler error return. | | 11 | `func(fiber.Req, fiber.Res, func(error) error)` | Express-style middleware with an error-accepting `next` callback that returns an error. | | 12 | `func(fiber.Req, fiber.Res, func(error) error) error` | Express-style middleware with an error-accepting `next` callback that returns an error and handler error return. | | 13 | `http.HandlerFunc` | Standard-library handler function adapted through `fasthttpadaptor`. | | 14 | `http.Handler` | Standard-library handler implementation; pointer receivers must be non-nil. | | 15 | `func(http.ResponseWriter, *http.Request)` | Standard-library function handlers via `fasthttpadaptor`. | | 16 | `fasthttp.RequestHandler` | Direct fasthttp handler without error return. | | 17 | `func(*fasthttp.RequestCtx) error` | fasthttp handler that returns an error to Fiber. | ### Route chaining `RouteChain` is a new helper inspired by [`Express`](https://expressjs.com/en/api.html#app.route) that makes it easy to declare a stack of handlers on the same path, while the existing `Route` helper stays available for prefix encapsulation. ```go RouteChain(path string) Register ```
Example ```go app.RouteChain("/api").RouteChain("/user/:id?") .Get(func(c fiber.Ctx) error { // Get user return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) }) .Post(func(c fiber.Ctx) error { // Create user return c.JSON(fiber.Map{"message": "User created"}) }) .Put(func(c fiber.Ctx) error { // Update user return c.JSON(fiber.Map{"message": "User updated", "id": c.Params("id")}) }) .Delete(func(c fiber.Ctx) error { // Delete user return c.JSON(fiber.Map{"message": "User deleted", "id": c.Params("id")}) }) ```
You can find more information about `app.RouteChain` and `app.Route` in the API documentation ([RouteChain](./api/app#routechain), [Route](./api/app#route)). Named routes retrieved with `app.GetRoute(name)` also support `route.URL(params)` for generating relative URLs directly from the route definition, including parameter substitution for named, wildcard (`*`), and plus (`+`) segments. ### Domain routing `Domain` creates a router scoped to a specific hostname pattern. Routes registered through the returned `Router` only match requests whose hostname (from `c.Hostname()`) matches the pattern. When `TrustProxy` is enabled and the proxy is trusted (as defined by [`TrustProxyConfig`](./api/app#trustproxyconfig)), the hostname may be derived from the `X-Forwarded-Host` header. Be sure to configure `TrustProxyConfig` to restrict which proxies are trusted and prevent header spoofing when enabling `TrustProxy`. The pattern can contain parameters prefixed with `:`, accessible via `fiber.DomainParam`. Domain routing has **zero performance impact** on routes that don't use it because the hostname check is applied as a handler wrapper, not a change to the core router. > **Note:** Because domain filtering happens at handler-execution time, Fiber's `405 Method Not Allowed` responses may advertise methods from domain-scoped routes even when the requesting host does not match. This is a known trade-off of the handler-wrapping approach. > > When mounting sub-applications via `Domain(...).Use(*fiber.App)`, routes are cloned at mount time. The same sub-app can safely be mounted on multiple domains, but routes added to the sub-app after mounting will not inherit domain filtering. Register all sub-app routes before mounting. ```go Domain(host string) Router ```
Example ```go app := fiber.New() // Static domain app.Domain("api.example.com").Get("/users", func(c fiber.Ctx) error { return c.SendString("API users") }) // Domain with parameter app.Domain(":user.blog.example.com").Get("/", func(c fiber.Ctx) error { user := fiber.DomainParam(c, "user") return c.SendString(user + "'s blog") }) // Domain with groups api := app.Domain("api.example.com") v1 := api.Group("/v1") v1.Get("/posts", listPosts) // Domain with middleware admin := app.Domain("admin.example.com") admin.Use(authMiddleware) admin.Get("/dashboard", dashboardHandler) // Mount sub-applications on domain routers subApp := fiber.New() subApp.Get("/users", listUsers) app.Domain("api.example.com").Use("/api", subApp) // Fallback for unmatched domains app.Get("/", func(c fiber.Ctx) error { return c.SendString("default site") }) ```
### Automatic HEAD routes for GET Fiber now auto-registers a `HEAD` route whenever you add a `GET` route. The generated handler chain matches the `GET` chain so status codes and headers stay in sync while the response body remains empty, ensuring `HEAD` clients observe the same metadata as a `GET` consumer. ```go title="GET now enables HEAD automatically" app := fiber.New() app.Get("/health", func(c fiber.Ctx) error { c.Set("X-Service", "api") return c.SendString("OK") }) // HEAD /health reuses the GET middleware chain and returns headers only. ``` You can still register explicit `HEAD` handlers for any `GET` route, and they continue to win when you add them: ```go title="Override the generated HEAD handler" app.Head("/health", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) }) ``` Prefer to manage `HEAD` routes yourself? Disable the feature through `fiber.Config.DisableHeadAutoRegister`: ```go title="Disable automatic HEAD registration" handler := func(c fiber.Ctx) error { c.Set("X-Service", "api") return c.SendString("OK") } app := fiber.New(fiber.Config{DisableHeadAutoRegister: true}) app.Get("/health", handler) // HEAD /health now returns 405 unless you add it manually. ``` Auto-generated `HEAD` routes appear in tooling such as `app.Stack()` and cover the same routing scenarios as their `GET` counterparts, including groups, mounted apps, dynamic parameters, and static file handlers. ### QUERY method (RFC 10008) Fiber now supports the HTTP `QUERY` method ([RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html)) as a first-class verb. `QUERY` is safe and idempotent like `GET`, but allows a request body for complex queries. ```go title="Register a QUERY route" app := fiber.New() app.Query("/search", func(c fiber.Ctx) error { // QUERY carries a request body, unlike GET. return c.Send(c.Body()) }) ``` `Query` is available on `App`, `Group`, the route-chaining `Register` interface, and domain routers, plus the HTTP client (`Request.Query`, `Client.Query`, and the package-level `Query`). `fiber.IsMethodSafe` and `fiber.IsMethodIdempotent` both return `true` for `QUERY`, so middleware that keys off method safety (CSRF, idempotency, early data) treats it like the other safe methods. The cache middleware can cache `QUERY` responses when `QUERY` is added to `Config.Methods`; the default key generator folds the request body into the cache key so different bodies on the same URL do not collide. ### Middleware registration We have aligned our method for middlewares closer to [`Express`](https://expressjs.com/en/api.html#app.use) and now also support the [`Use`](./api/app#use) of multiple prefixes. Prefix matching is now stricter: partial matches must end at a slash boundary (or be an exact match). This keeps `/api` middleware from running on `/apiv1` while still allowing `/api/:version` style patterns that leverage route parameters, optional segments, or wildcards. Registering a subapp is now also possible via the [`Use`](./api/app#use) method instead of the old `app.Mount` method.
Example ```go // register multiple prefixes app.Use([]string{"/v1", "/v2"}, func(c fiber.Ctx) error { // Middleware for /v1 and /v2 return c.Next() }) // define subapp api := fiber.New() api.Get("/user", func(c fiber.Ctx) error { return c.SendString("User") }) // register subapp app.Use("/api", api) ```
To enable the routing changes above we had to slightly adjust the signature of the `Add` method. ```diff - Add(method, path string, handlers ...Handler) Router + Add(methods []string, path string, handler any, handlers ...any) Router ``` ### Test Config The `app.Test()` method now allows users to customize their test configurations:
Example ```go // Create a test app with a handler to test app := fiber.New() app.Get("/", func(c fiber.Ctx) { return c.SendString("hello world") }) // Define the HTTP request and custom TestConfig to test the handler req := httptest.NewRequest(MethodGet, "/", nil) testConfig := fiber.TestConfig{ Timeout: 0, FailOnTimeout: false, } // Test the handler using the request and testConfig resp, err := app.Test(req, testConfig) ```
To provide configurable testing capabilities, we had to change the signature of the `Test` method. ```diff - Test(req *http.Request, timeout ...time.Duration) (*http.Response, error) + Test(req *http.Request, config ...fiber.TestConfig) (*http.Response, error) ``` The `TestConfig` struct provides the following configuration options: - `Timeout`: The duration to wait before timing out the test. Use 0 for no timeout. - `FailOnTimeout`: Controls the behavior when a timeout occurs: - When true, the test will return an `os.ErrDeadlineExceeded` if the test exceeds the `Timeout` duration. - When false, the test will return the partial response received before timing out. If a custom `TestConfig` isn't provided, then the following will be used: ```go testConfig := fiber.TestConfig{ Timeout: time.Second, FailOnTimeout: true, } ``` **Note:** Using this default is **NOT** the same as providing an empty `TestConfig` as an argument to `app.Test()`. An empty `TestConfig` is the equivalent of: ```go testConfig := fiber.TestConfig{ Timeout: 0, FailOnTimeout: false, } ``` ### Constraint System The internal constraint system has been unified into a single `ConstraintHandler` interface. Built-in and custom constraints are now treated uniformly through this interface, with an optional `ConstraintAnalyzer` phase for precomputation at route registration time. ```go type ConstraintHandler interface { Name() string Execute(param string, data []any) bool } type ConstraintAnalyzer interface { Analyze(args []string) ([]any, error) } ``` Key improvements: - **Zero per-request parsing**: `strconv.Atoi`, `time.Parse` layouts, and regex compilation happen once at registration via `Analyze()`, not on every request. - **Single dispatch**: The previous `TypeConstraint` bitmask switch has been replaced by a single `handler.Execute()` call. - **Backward compatible**: Existing `CustomConstraint` implementations continue to work unchanged. The `CustomConstraint` interface, `RegisterCustomConstraint()` API, and `CheckConstraint()` method are all preserved. The `TypeConstraint` type, `Constraint.ID`, and `Constraint.RegexCompiler` fields are retained but deprecated. ## 🧠 Context ### New Features - Cookie now allows Partitioned cookies for [CHIPS](https://developers.google.com/privacy-sandbox/3pcd/chips) support. CHIPS (Cookies Having Independent Partitioned State) is a feature that improves privacy by allowing cookies to be partitioned by top-level site, mitigating cross-site tracking. - Cookie automatic security enforcement: When setting a cookie with `SameSite=None`, Fiber automatically sets `Secure=true` as required by RFC 6265bis and modern browsers (Chrome, Firefox, Safari). This ensures compliance with the "None" SameSite policy. See [Mozilla docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#none) and [Chrome docs](https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure) for details. - `Ctx` now implements the [context.Context](https://pkg.go.dev/context#Context) interface, replacing the former `UserContext` helpers. ### New Methods - **AutoFormat**: Similar to Express.js, automatically formats the response based on the request's `Accept` header. - **Deadline**: For implementing `context.Context`. - **Done**: For implementing `context.Context`. - **Err**: For implementing `context.Context`. - **Host**: Similar to Express.js, returns the host name of the request. - **Port**: Similar to Express.js, returns the port number of the request. - **IsProxyTrusted**: Checks the trustworthiness of the remote IP. - **Reset**: Resets context fields for server handlers. - **Schema**: Similar to Express.js, returns the schema (HTTP or HTTPS) of the request. - **SendEarlyHints**: Sends `HTTP 103 Early Hints` status code with `Link` headers so browsers can preload resources while the final response is being prepared. - **SendStream**: Similar to Express.js, sends a stream as the response. - **SendStreamWriter**: Sends a stream using a writer function. - **SendString**: Similar to Express.js, sends a string as the response. - **String**: Similar to Express.js, converts a value to a string. - **Value**: For implementing `context.Context`. Returns request-scoped value from Locals. - **Context()**: Returns a `context.Context` that can be used outside the handler. - **SetContext**: Sets the base `context.Context` returned by `Context()` for propagating deadlines or values. - **ViewBind**: Binds data to a view, replacing the old `Bind` method. - **CBOR**: Introducing [CBOR](https://cbor.io/) binary encoding format for both request & response body. CBOR is a binary data serialization format which is both compact and efficient, making it ideal for use in web applications. - **MsgPack**: Introducing [MsgPack](https://msgpack.org/) binary encoding format for both request & response body. MsgPack is a binary serialization format that is more efficient than JSON, making it ideal for high-performance applications. - **Drop**: Terminates the client connection silently without sending any HTTP headers or response body. This can be used for scenarios where you want to block certain requests without notifying the client, such as mitigating DDoS attacks or protecting sensitive endpoints from unauthorized access. - **End**: Similar to Express.js, immediately flushes the current response and closes the underlying connection. - **AcceptsLanguagesExtended**: Matches language ranges using RFC 4647 Extended Filtering with wildcard subtags. - **FullURL**: Returns the full request URL (scheme + host + original URL). - **RequestID**: Returns the request identifier from the response or request headers. - **UserAgent**: Returns the `User-Agent` request header. - **Referer**: Returns the `Referer` request header. - **AcceptLanguage**: Returns the `Accept-Language` request header. - **AcceptEncoding**: Returns the `Accept-Encoding` request header. - **HasHeader**: Reports whether the request includes a header with the given key. - **MediaType**: Returns the MIME type from the `Content-Type` header without parameters. - **Charset**: Returns the `charset` parameter from the `Content-Type` header. - **IsJSON**: Reports whether the `Content-Type` header is JSON. - **IsForm**: Reports whether the `Content-Type` header is form-encoded. - **IsMultipart**: Reports whether the `Content-Type` header is multipart form data. - **AcceptsJSON**: Reports whether the `Accept` header allows JSON. - **AcceptsHTML**: Reports whether the `Accept` header allows HTML. - **AcceptsXML**: Reports whether the `Accept` header allows XML. - **AcceptsEventStream**: Reports whether the `Accept` header allows `text/event-stream`. - **Matched**: Detects when the current request path matched a registered route. - **IsMiddleware**: Indicates if the current handler was registered as middleware. - **HasBody**: Quickly checks whether the request includes a body. - **OverrideParam**: Overwrites the value of an existing route parameter, or does nothing if the parameter does not exist - **IsWebSocket**: Reports if the request attempts a WebSocket upgrade. - **IsPreflight**: Identifies CORS preflight requests before handlers run. ### Removed Methods - **AllParams**: Use `c.Bind().URI()` instead. - **ParamsInt**: Use `Params` with generic types. - **QueryBool**: Use `Query` with generic types. - **QueryFloat**: Use `Query` with generic types. - **QueryInt**: Use `Query` with generic types. - **BodyParser**: Use `c.Bind().Body()` instead. - **CookieParser**: Use `c.Bind().Cookie()` instead. - **ParamsParser**: Use `c.Bind().URI()` instead. - **RedirectToRoute**: Use `c.Redirect().Route()` instead. - **RedirectBack**: Use `c.Redirect().Back()` instead. - **ReqHeaderParser**: Use `c.Bind().Header()` instead. - **UserContext**: Removed. `Ctx` itself now satisfies `context.Context`; pass `c` directly where a `context.Context` is required. - **SetUserContext**: Removed. Use `SetContext` and `Context()` or `context.WithValue` on `c` to store additional request-scoped values. ### Changed Methods - **Bind**: Now used for binding instead of view binding. Use `c.ViewBind()` for view binding. - **Format**: Parameter changed from `body any` to `handlers ...ResFmt`. - **Redirect**: Use `c.Redirect().To()` instead. - **SendFile**: Now supports different configurations using a config parameter. - **Attachment and Download**: Non-ASCII filenames now use `filename*` as specified by [RFC 6266](https://www.rfc-editor.org/rfc/rfc6266) and [RFC 8187](https://www.rfc-editor.org/rfc/rfc8187). The `filename` parameter is now emitted as a plain RFC 9110 quoted-string instead of being URL-encoded: `c.Download("report 2024.txt")` produces `filename="report 2024.txt"` (previously `filename="report+2024.txt"`), with quotes and backslashes escaped as quoted-pairs, so browsers save files under their real names. - **Context()**: Renamed to `RequestCtx()` to access the underlying `fasthttp.RequestCtx`. - **IP()**: When `EnableIPValidation` is `true` and `TrustProxyConfig` is set, `c.IP()` now walks the `X-Forwarded-For` chain from right to left and returns the first non-trusted IP, instead of the leftmost syntactically valid IP. This closes an IP-spoofing vector where an attacker could prepend a fake address and have it returned by `c.IP()`. Apps with `EnableIPValidation = false` (the default) are unaffected. See [`Ctx.IP`](./api/ctx.md#ip) and the [reverse proxy guide](./guide/reverse-proxy.md#getting-the-real-client-ip-address) for details. ### SendEarlyHints `SendEarlyHints` sends an informational [`103 Early Hints`](https://developer.chrome.com/docs/web-platform/early-hints) response with `Link` headers based on the provided `hints` argument. This allows a browser to start preloading assets while the server is still preparing the final response. ```go hints := []string{"; rel=preload; as=script"} app.Get("/early", func(c fiber.Ctx) error { if err := c.SendEarlyHints(hints); err != nil { return err } return c.SendString("done") }) ``` Older HTTP/1.1 clients may ignore these interim responses or handle them inconsistently. ### SendStreamWriter In v3, we introduced support for buffered streaming with the addition of the `SendStreamWriter` method: ```go func (c Ctx) SendStreamWriter(streamWriter func(w *bufio.Writer)) error ``` With this new method, you can implement: - Server-Side Events (SSE) - Large file downloads - Live data streaming ```go 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") return c.SendStreamWriter(func(w *bufio.Writer) { for { fmt.Fprintf(w, "event: my-event\n") fmt.Fprintf(w, "data: Hello SSE\n\n") if err := w.Flush(); err != nil { log.Print("Client disconnected!") return } } }) }) ``` You can find more details about this feature in [/docs/api/ctx.md](./api/ctx.md). ### Drop In v3, we introduced support to silently terminate requests through `Drop`. ```go func (c Ctx) Drop() error ``` With this method, you can: - Block certain requests without notifying the client to mitigate DDoS attacks - Protect sensitive endpoints from unauthorized access without leaking errors. :::caution While this feature adds the ability to drop connections, it is still **highly recommended** to use additional measures (such as **firewalls**, **proxies**, etc.) to further protect your server endpoints by blocking malicious connections before the server establishes a connection. ::: ```go app.Get("/", func(c fiber.Ctx) error { if c.IP() == "192.168.1.1" { return c.Drop() } return c.SendString("Hello World!") }) ``` You can find more details about this feature in [/docs/api/ctx.md](./api/ctx.md). ### End In v3, we introduced a new method to match the Express.js API's `res.end()` method. ```go func (c Ctx) End() error ``` With this method, you can: - Stop middleware from controlling the connection after a handler further up the method chain by immediately flushing the current response and closing the connection. - Use `return c.End()` as an alternative to `return nil` ```go app.Use(func (c fiber.Ctx) error { err := c.Next() if err != nil { log.Println("Got error: %v", err) return c.SendString(err.Error()) // Will be unsuccessful since the response ended below } return nil }) app.Get("/hello", func (c fiber.Ctx) error { query := c.Query("name", "") if query == "" { _ = c.SendString("You don't have a name?") _ = c.End() // Closes the underlying connection; errors intentionally ignored return errors.New("No name provided") } return c.SendString("Hello, " + query + "!") }) ``` --- ## 📎 Binding Fiber v3 introduces a new binding mechanism that simplifies the process of binding request data to structs. The new binding system supports binding from various sources such as URL parameters, query parameters, headers, and request bodies. This unified approach makes it easier to handle different types of request data in a consistent manner. ### New Features - Unified binding from URL parameters, query parameters, headers, and request bodies. - Support for custom binders and constraints. - Improved error handling and validation. - Support multipart file binding for `*multipart.FileHeader`, `*[]*multipart.FileHeader`, and `[]*multipart.FileHeader` field types. - Support for unified binding (`Bind().All()`) with defined precedence order: (URI -> Body -> Query -> Headers -> Cookies). [Learn more](./api/bind.md#all). - Support MsgPack binding for request body.
Example ```go type User struct { ID int `uri:"id"` Name string `json:"name"` Email string `json:"email"` } app.Post("/user/:id", func(c fiber.Ctx) error { var user User if err := c.Bind().Body(&user); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(user) }) ``` In this example, the `Bind` method is used to bind the request body to the `User` struct. The `Body` method of the `Bind` class performs the actual binding.
## 🔬 Extractors Package Fiber v3 introduces a new shared `extractors` package that consolidates value extraction utilities previously duplicated across middleware packages. This package provides a unified API for extracting values from headers, cookies, query parameters, form data, and URL parameters with built-in chain/fallback logic and security considerations. ### Key Features - **Unified API**: Single package for extracting values from headers, cookies, query parameters, form data, and URL parameters - **Chain Logic**: Built-in fallback mechanism to try multiple extraction sources in order - **Source Awareness**: Source inspection capabilities for security-sensitive operations - **Type Safety**: Strongly typed extraction with proper error handling - **Performance**: Optimized extraction functions with minimal overhead ### Available Extractors - `FromAuthHeader(authScheme string)`: Extract from Authorization header with scheme support - `FromCookie(key string)`: Extract from HTTP cookies - `FromParam(param string)`: Extract from URL path parameters - `FromForm(param string)`: Extract from form data - `FromHeader(header string)`: Extract from custom HTTP headers - `FromQuery(param string)`: Extract from URL query parameters - `FromCustom(key string, extractor func(c fiber.Ctx) (string, error))`: Define custom extraction logic with metadata - `Chain(extractors ...Extractor)`: Chain multiple extractors with fallback logic ### Usage Example ```go import "github.com/gofiber/fiber/v3/extractors" // Extract API key from multiple sources with fallback apiKeyExtractor := extractors.Chain( extractors.FromHeader("X-API-Key"), extractors.FromQuery("api_key"), extractors.FromCookie("api_key"), ) app.Use(func(c fiber.Ctx) error { apiKey, err := apiKeyExtractor.Extract(c) if err != nil { return c.Status(401).SendString("API key required") } // Use apiKey for authentication return c.Next() }) ``` ### Migration from Middleware-Specific Extractors Middleware packages in Fiber v3 now use the shared extractors package instead of maintaining their own extraction logic. This provides: - **Code Deduplication**: Eliminates ~500+ lines of duplicated extraction code - **Consistency**: Standardized extraction behavior across all middleware - **Maintainability**: Single source of truth for extraction logic - **Security**: Unified security considerations and warnings ## 🔄 Redirect Fiber v3 enhances the redirect functionality by introducing new methods and improving existing ones. The new redirect methods provide more flexibility and control over the redirection process. ### New Methods - `Redirect().To()`: Redirects to a specific URL. - `Redirect().Route()`: Redirects to a named route. - `Redirect().Back()`: Redirects to the previous URL.
Example ```go app.Get("/old", func(c fiber.Ctx) error { return c.Redirect().To("/new") }) app.Get("/new", func(c fiber.Ctx) error { return c.SendString("Welcome to the new route!") }) ```
### Changed behavior :::info The default redirect status code has been updated from `302 Found` to `303 See Other` to ensure more consistent behavior across different browsers. ::: ## 🌎 Client package The Gofiber client has been completely rebuilt. It includes numerous new features such as Cookiejar, request/response hooks, and more. You can take a look to [client docs](./client/rest.md) to see what's new with the client. ### Configuration improvements The v3 client centralizes common configuration on the client instance and lets you override it per request with `client.Config`. You can define base URLs, defaults (headers, cookies, path parameters, timeouts), and toggle path normalization once, while still using axios-style helpers for each call. ```go cc := client.New(). SetBaseURL("https://api.service.local"). AddHeader("Authorization", "Bearer "). SetTimeout(5 * time.Second). SetPathParam("tenant", "acme") resp, err := cc.Get("/users/:tenant/:id", client.Config{ PathParam: map[string]string{"id": "42"}, Param: map[string]string{"include": "profile"}, DisablePathNormalizing: true, }) if err != nil { panic(err) } defer resp.Close() fmt.Println(resp.StatusCode(), resp.String()) ``` ### Fasthttp transport integration - `client.NewWithHostClient` and `client.NewWithLBClient` allow you to plug existing `fasthttp` clients directly into Fiber while keeping retries, redirects, and hook logic consistent. - Dialer, TLS, and proxy helpers now update every host client inside a load balancer, so complex pools inherit the same configuration. - The Fiber client exposes `Do`, `DoTimeout`, `DoDeadline`, and `CloseIdleConnections`, matching the surface area of the wrapped fasthttp transports. ## 🧰 Generic functions Fiber v3 introduces new generic functions that provide additional utility and flexibility for developers. These functions are designed to simplify common tasks and improve code readability. ### New Generic Functions - **StoreInContext**: Stores request-scoped values in both `c.Locals()` and the request `context.Context`, so the same value can be read through middleware `FromContext` helpers and direct locals access. - **Convert**: Converts a value with a specified converter function and default value. - **Locals**: Retrieves or sets local values within a request context. - **Params**: Retrieves route parameters and can handle various types of route parameters. - **Query**: Retrieves the value of a query parameter from the request URI and can handle various types of query parameters. - **GetReqHeader**: Returns the HTTP request header specified by the field and can handle various types of header values. `fiber.Config.PassLocalsToContext` is now available to control whether `StoreInContext` also synchronizes values with request `context.Context` for Fiber-backed contexts. The default is `false` for backward compatibility. `ValueFromContext` continues reading Fiber-backed values from `c.Locals()`. ### Example
Convert ```go package main import ( "strconv" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/convert", func(c fiber.Ctx) error { value, err := fiber.Convert[int](c.Query("value"), strconv.Atoi, 0) if err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } return c.JSON(value) }) app.Listen(":3000") } ``` ```sh curl "http://localhost:3000/convert?value=123" # Output: 123 curl "http://localhost:3000/convert?value=abc" # Output: "failed to convert: strconv.Atoi: parsing \"abc\": invalid syntax" ```
Locals ```go package main import ( "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Use("/user/:id", func(c fiber.Ctx) error { // ask database for user // ... // set local values from database fiber.Locals[string](c, "user", "john") fiber.Locals[int](c, "age", 25) // ... return c.Next() }) app.Get("/user/*", func(c fiber.Ctx) error { // get local values name := fiber.Locals[string](c, "user") age := fiber.Locals[int](c, "age") // ... return c.JSON(fiber.Map{"name": name, "age": age}) }) app.Listen(":3000") } ``` ```sh curl "http://localhost:3000/user/5" # Output: {"name":"john","age":25} ```
Params ```go package main import ( "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/params/:id", func(c fiber.Ctx) error { id := fiber.Params[int](c, "id", 0) return c.JSON(id) }) app.Listen(":3000") } ``` ```sh curl "http://localhost:3000/params/123" # Output: 123 curl "http://localhost:3000/params/abc" # Output: 0 ```
Query ```go package main import ( "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/query", func(c fiber.Ctx) error { age := fiber.Query[int](c, "age", 0) return c.JSON(age) }) app.Listen(":3000") } ``` ```sh curl "http://localhost:3000/query?age=25" # Output: 25 curl "http://localhost:3000/query?age=abc" # Output: 0 ```
GetReqHeader ```go package main import ( "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/header", func(c fiber.Ctx) error { userAgent := fiber.GetReqHeader[string](c, "User-Agent", "Unknown") return c.JSON(userAgent) }) app.Listen(":3000") } ``` ```sh curl -H "User-Agent: CustomAgent" "http://localhost:3000/header" # Output: "CustomAgent" curl "http://localhost:3000/header" # Output: "Unknown" ```
## 🛠️ Utils {#utils} Fiber v3 removes the built-in `utils` directory and now imports utility helpers from the separate [`github.com/gofiber/utils/v2`](https://github.com/gofiber/utils) module. See the [migration guide](#utils-migration) for detailed replacement steps and examples. The `github.com/gofiber/utils` module also introduces new helpers like `ParseInt`, `ParseUint`, `Walk`, `ReadFile`, and `Timestamp`. ## 🧩 Services Fiber v3 introduces a new feature called Services. This feature allows developers to quickly start services that the application depends on, removing the need to manually provision things like database servers, caches, or message brokers, to name a few. ### Example
Adding a service ```go package main import ( "context" "github.com/gofiber/fiber/v3" ) type myService struct { img string // ... } // Start initializes and starts the service. It implements the [fiber.Service] interface. func (s *myService) Start(ctx context.Context) error { // start the service return nil } // String returns a string representation of the service. // It is used to print a human-readable name of the service in the startup message. // It implements the [fiber.Service] interface. func (s *myService) String() string { return s.img } // State returns the current state of the service. // It implements the [fiber.Service] interface. func (s *myService) State(ctx context.Context) (string, error) { return "running", nil } // Terminate stops and removes the service. It implements the [fiber.Service] interface. func (s *myService) Terminate(ctx context.Context) error { // stop the service return nil } func main() { cfg := &fiber.Config{} cfg.Services = append(cfg.Services, &myService{img: "postgres:latest"}) cfg.Services = append(cfg.Services, &myService{img: "redis:latest"}) app := fiber.New(*cfg) // ... } ```
Output ```sh $ go run . -v _______ __ / ____(_) /_ ___ _____ / /_ / / __ \/ _ \/ ___/ / __/ / / /_/ / __/ / /_/ /_/_.___/\___/_/ v3.0.0 -------------------------------------------------- INFO Server started on: http://127.0.0.1:3000 (bound on host 0.0.0.0 and port 3000) INFO Services: 2 INFO 🧩 [ RUNNING ] postgres:latest INFO 🧩 [ RUNNING ] redis:latest INFO Total handlers count: 2 INFO Prefork: Disabled INFO PID: 12279 INFO Total process count: 1 ```
## 📃 Log `fiber.AllLogger[T]` interface now has a new generic type parameter `T` and a method called `Logger`. This method can be used to get the underlying logger instance from the Fiber logger middleware. This is useful when you want to configure the logger middleware with a custom logger and still want to access the underlying logger instance with the appropriate type. You can find more details about this feature in [/docs/api/log.md](./api/log.md#logger). `logger.Config` now supports a new field called `ForceColors`. This field allows you to force the logger to always use colors, even if the output is not a terminal. This is useful when you want to ensure that the logs are always colored, regardless of the output destination. ```go package main import "github.com/gofiber/fiber/v3/middleware/logger" app.Use(logger.New(logger.Config{ ForceColors: true, })) ``` ## 📦 Storage Interface The storage interface has been updated to include new subset of methods with `WithContext` suffix. These methods allow you to pass a context to the storage operations, enabling better control over timeouts and cancellation if needed. This is particularly useful when storage implementations used outside of the Fiber core, such as in background jobs or long-running tasks. **New Methods Signatures:** ```go // GetWithContext gets the value for the given key with a context. // `nil, nil` is returned when the key does not exist GetWithContext(ctx context.Context, key string) ([]byte, error) // SetWithContext stores the given value for the given key // with an expiration value, 0 means no expiration. // Empty key or value will be ignored without an error. SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error // DeleteWithContext deletes the value for the given key with a context. // It returns no error if the storage does not contain the key, DeleteWithContext(ctx context.Context, key string) error // ResetWithContext resets the storage and deletes all keys with a context. ResetWithContext(ctx context.Context) error ``` ## 🧬 Middlewares ### Important Change for Accessing Middleware Data In Fiber v3, many middlewares that previously set values in `c.Locals()` using string keys (e.g., `c.Locals("requestid")`) have been updated. To align with Go's context best practices and prevent key collisions, these middlewares now store their specific data in the request's context using unexported keys of custom types. This means that directly accessing these values via `c.Locals("some_string_key")` will no longer work for such middleware-provided data. **How to Access Middleware Data in v3:** Each affected middleware now provides dedicated exported functions to retrieve its specific data from the context. You should use these functions instead of relying on string-based lookups in `c.Locals()`. Examples include: - `requestid.FromContext(c)` - `csrf.TokenFromContext(c)` - `csrf.HandlerFromContext(c)` - `session.FromContext(c)` - `basicauth.UsernameFromContext(c)` - `keyauth.TokenFromContext(c)` When used with the Logger middleware, the recommended approach is to use the `CustomTags` feature of the logger, which allows you to call these specific `FromContext` functions. See the [Logger](#logger) section for more details. ### Adaptor The adaptor middleware has been significantly optimized for performance and efficiency. Key improvements include reduced response times, lower memory usage, and fewer memory allocations. These changes make the middleware more reliable and capable of handling higher loads effectively. Enhancements include the introduction of a `sync.Pool` for managing `fasthttp.RequestCtx` instances and better HTTP request and response handling between net/http and fasthttp contexts. Incoming body sizes now respect the Fiber app's configured `BodyLimit` (falling back to the default when unset) when running Fiber from `net/http` through the adaptor, returning `413 Request Entity Too Large` for oversized payloads. The adaptor also propagates the request's protocol version, normalized to Fiber's convention (`HTTP/2.0` → `HTTP/2`, `HTTP/3.0` → `HTTP/3`), so `c.Protocol()` reports the real version instead of always `HTTP/1.1`. Interim responses such as `SendEarlyHints`' `103` are silently skipped through the adaptor — there is no client connection to write them to — while the `Link` headers still reach the final response. | Payload Size | Metric | V2 | V3 | Percent Change | | ------------ | -------------- | ------------ | ----------- | -------------- | | 100KB | Execution Time | 1056 ns/op | 588.6 ns/op | -44.25% | | | Memory Usage | 2644 B/op | 254 B/op | -90.39% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 500KB | Execution Time | 1061 ns/op | 562.9 ns/op | -46.94% | | | Memory Usage | 2644 B/op | 248 B/op | -90.62% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 1MB | Execution Time | 1080 ns/op | 629.7 ns/op | -41.68% | | | Memory Usage | 2646 B/op | 267 B/op | -89.91% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 5MB | Execution Time | 1093 ns/op | 540.3 ns/op | -50.58% | | | Memory Usage | 2654 B/op | 254 B/op | -90.43% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 10MB | Execution Time | 1044 ns/op | 533.1 ns/op | -48.94% | | | Memory Usage | 2665 B/op | 258 B/op | -90.32% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 25MB | Execution Time | 1069 ns/op | 540.7 ns/op | -49.42% | | | Memory Usage | 2706 B/op | 289 B/op | -89.32% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | | 50MB | Execution Time | 1137 ns/op | 554.6 ns/op | -51.21% | | | Memory Usage | 2734 B/op | 298 B/op | -89.10% | | | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | ### BasicAuth The BasicAuth middleware now validates the `Authorization` header more rigorously and sets security-focused response headers. Passwords must be provided in **hashed** form (e.g. SHA-256 or bcrypt) rather than plaintext. The default challenge includes the `charset="UTF-8"` parameter and disables caching. Responses also set a `Vary: Authorization` header to prevent caching based on credentials. Passwords are no longer stored in the request context. A `Charset` option controls the value used in the challenge header. A new `HeaderLimit` option restricts the maximum length of the `Authorization` header (default: `8192` bytes). The `Authorizer` function now receives the current `fiber.Ctx` as a third argument, allowing credential checks to incorporate request context. ### Cache We are excited to introduce a new option in our caching middleware: Cache Invalidator. This feature provides greater control over cache management, allowing you to define custom conditions for invalidating cache entries. The middleware now emits `Cache-Control` headers by default via the new `DisableCacheControl` flag, increases the default `Expiration` from `1 minute` to `5 minutes`, and applies a new `MaxBytes` limit of `1 MB` (previously unlimited). Additionally, the caching middleware has been optimized to avoid caching non-cacheable status codes, as defined by the [HTTP standards](https://datatracker.ietf.org/doc/html/rfc7231#section-6.1). This improvement enhances cache accuracy and reduces unnecessary cache storage usage. Cached responses now include an RFC-compliant Age header, providing a standardized indication of how long a response has been stored in cache since it was originally generated. This enhancement improves HTTP compliance and facilitates better client-side caching strategies. Cache keys are now redacted in logs and error messages by default, and a `DisableValueRedaction` boolean (default `false`) lets you opt out when you need the raw value for troubleshooting. The default cache key strategy was also hardened. Instead of path-only behavior, keys now use structured request dimensions: method partitioning, path, canonical query string, and selected representation headers (`Accept`, `Accept-Encoding`, `Accept-Language`). This avoids collisions such as `/items?id=1` vs `/items?id=2` while keeping key generation deterministic. New config fields were added for explicit control: `DisableQueryKeys`, `KeyHeaders`, `KeyCookies`, and `DisableVaryHeaders`. As a security/performance default, request body/form values are not part of the default cache key. Cache handling is limited to `GET` and `HEAD` requests by default, configurable via the `Methods` field. :::note The deprecated `Store` and `Key` options have been removed in v3. Use `Storage` and `KeyGenerator` instead. ::: ### ResponseTime A new response time middleware measures how long each request takes to process and adds the duration to the response headers. By default it writes the elapsed time to `X-Response-Time`, and you can change the header name. A `Next` hook lets you skip endpoints such as health checks. ### CORS We've made some changes to the CORS middleware to improve its functionality and flexibility. Here's what's new: #### New Struct Fields - `Config.AllowPrivateNetwork`: This new field is a boolean that allows you to control whether private networks are allowed. This is related to the [Private Network Access (PNA)](https://wicg.github.io/private-network-access/) specification from the [Web Incubator Community Group (WICG)](https://wicg.io/). When set to `true`, the CORS middleware will allow CORS preflight requests from private networks and respond with the `Access-Control-Allow-Private-Network: true` header. This could be useful in development environments or specific use cases, but should be done with caution due to potential security risks. #### Updated Struct Fields We've updated several fields from a single string (containing comma-separated values) to slices, allowing for more explicit declaration of multiple values. Here are the updated fields: - `Config.AllowOrigins`: Now accepts a slice of strings, each representing an allowed origin. - `Config.AllowMethods`: Now accepts a slice of strings, each representing an allowed method. - `Config.AllowHeaders`: Now accepts a slice of strings, each representing an allowed header. - `Config.ExposeHeaders`: Now accepts a slice of strings, each representing an exposed header. Additionally, panic messages and logs redact misconfigured origins by default, and a `DisableValueRedaction` flag (default `false`) lets you reveal them when necessary. ### Compression - Added support for `zstd` compression alongside `gzip`, `deflate`, and `brotli`. - Strong `ETag` values are now recomputed for compressed payloads so validators remain accurate. - Compression is bypassed for responses that already specify `Content-Encoding`, for range requests or `206` statuses, and when either side sends `Cache-Control: no-transform`. - `HEAD` requests still negotiate compression so `Content-Encoding`, `Content-Length`, `ETag`, and `Vary` match a corresponding `GET`, but the body is omitted. - `Vary: Accept-Encoding` is merged into responses even when compression is skipped, preventing caches from mixing encoded and unencoded variants. - Decoding compressed request bodies now enforces the app `BodyLimit` through fasthttp `WithLimit` helpers, including when the compression middleware is active. - Multipart form parsing now enforces the app `BodyLimit` by using fasthttp `MultipartFormWithLimit`. ### CSRF The `Expiration` field in the CSRF middleware configuration has been renamed to `IdleTimeout` to better describe its functionality. Additionally, the default value has been reduced from 1 hour to 30 minutes. CSRF now redacts tokens and storage keys by default and exposes a `DisableValueRedaction` toggle (default `false`) if you must surface those values in diagnostics. The CSRF middleware now validates the [`Sec-Fetch-Site`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site) header for unsafe HTTP methods. When present, requests with invalid `Sec-Fetch-Site` values (not one of "same-origin", "none", "same-site", or "cross-site") are rejected with `ErrFetchSiteInvalid`. Valid or absent headers proceed to standard origin and token validation checks, providing an early gate to catch malformed requests while maintaining compatibility with legitimate cross-site traffic. ### Idempotency Idempotency middleware now redacts keys by default and offers a `DisableValueRedaction` configuration flag (default `false`) to expose them when debugging. ### EncryptCookie - Added support for specifying key length when using `encryptcookie.GenerateKey(length)`. Keys must be base64-encoded and may be 16, 24, or 32 bytes when decoded, supporting AES-128, AES-192, and AES-256 (default). - Custom encryptor and decryptor callbacks now receive the cookie name. The default AES-GCM helpers bind it as additional authenticated data (AAD) so ciphertext cannot be replayed under a different cookie. - **Breaking change:** Custom encryptor/decryptor hooks now accept the cookie name as their first argument. Update overrides like: ```go // Before Encryptor func(value, key string) (string, error) Decryptor func(value, key string) (string, error) // After Encryptor func(name, value, key string) (string, error) Decryptor func(name, value, key string) (string, error) ``` ### Favicon The favicon middleware now caps cached favicon assets with a configurable `MaxBytes` limit (default `1 MiB`) and uses a limited reader to guard against oversized files when loading from disk. ### EnvVar The `ExcludeVars` field has been removed from the EnvVar middleware configuration. When upgrading, remove any references to this field and explicitly list the variables you wish to expose using `ExportVars`. ### Filesystem The filesystem middleware was removed to reduce confusion with the static middleware. The static middleware now covers the functionality of both. Review the [static middleware](./middleware/static.md) docs or the [migration guide](#-migration-guide) for the updated usage. ### Healthcheck The healthcheck middleware has been simplified into a single generic probe handler. No endpoints are registered automatically. Register the middleware on each route you need—using helpers like `healthcheck.LivenessEndpoint`, `healthcheck.ReadinessEndpoint`, or `healthcheck.StartupEndpoint`—and optionally supply a `Probe` function to determine the service's health. This approach lets you expose any number of health check routes. Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.md) or the [general migration guide](#-migration-guide) to review the changes. ### KeyAuth The keyauth middleware was updated to introduce a configurable `Realm` field for the `WWW-Authenticate` header. The old string-based `KeyLookup` configuration has been replaced with an `Extractor` field. Use helper functions like `keyauth.FromHeader`, `keyauth.FromAuthHeader`, or `keyauth.FromCookie` to define where the key should be retrieved from. Multiple sources can be combined with `keyauth.Chain`. See the migration guide below. New `Challenge`, `Error`, `ErrorDescription`, `ErrorURI`, and `Scope` fields allow customizing the `WWW-Authenticate` header, returning Bearer error details, and specifying required scopes. `ErrorURI` values are validated as absolute, a default `ApiKey` challenge is emitted when using non-Authorization extractors, Bearer `error` values are validated, credentials must conform to RFC 7235 `token68` syntax, and `scope` values are checked against RFC 6750's `scope-token` format. The header is also emitted only after the status code is finalized. ### Logger New helper function called `LoggerToWriter` has been added to the logger middleware. This function allows you to use 3rd party loggers such as `logrus` or `zap` with the Fiber logger middleware without an extra adapter. For example, you can use `zap` with Fiber logger middleware like this: Logger configuration now uses `Stream` instead of `Output` for the destination writer, so update your logger middleware configuration when migrating to v3. Custom logger integrations should update any `LoggerFunc` implementations to the new signature that receives a pointer to the middleware config: `func(c fiber.Ctx, data *logger.Data, cfg *logger.Config) error`.
Example ```go package main import ( "github.com/gofiber/contrib/fiberzap/v2" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" "github.com/gofiber/fiber/v3/middleware/logger" ) func main() { // Create a new Fiber instance app := fiber.New() // Create a new zap logger which is compatible with Fiber AllLogger interface zap := fiberzap.NewLogger(fiberzap.LoggerConfig{ ExtraKeys: []string{"request_id"}, }) // Use the logger middleware with zerolog logger app.Use(logger.New(logger.Config{ Stream: logger.LoggerToWriter(zap, log.LevelDebug), })) // Define a route app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) // Start server on http://localhost:3000 app.Listen(":3000") } ```
:::note The deprecated `TagHeader` constant was removed. Use `TagReqHeader` when you need to log request headers. ::: #### Logging Middleware Values (e.g., Request ID) In Fiber v3, middleware (like `requestid`) now stores values in the request context using unexported keys of custom types. This aligns with Go's context best practices to prevent key collisions between packages. As a result, directly accessing these values using string keys with `c.Locals("your_key")` or in the logger format string with `${locals:your_key}` (e.g., `${locals:requestid}`) will no longer work for values set by such middleware. **Recommended Solution: `CustomTags`** The cleanest and most maintainable way to include these middleware-specific values in your logs is by using the `CustomTags` option in the logger middleware configuration. This allows you to define a custom function to retrieve the value correctly from the context.
Example: Logging Request ID with CustomTags ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/logger" "github.com/gofiber/fiber/v3/middleware/requestid" ) func main() { app := fiber.New() // Ensure requestid middleware is used before the logger app.Use(requestid.New()) app.Use(logger.New(logger.Config{ CustomTags: map[string]logger.LogFunc{ "requestid": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) { // Retrieve the request ID using the middleware's specific function return output.WriteString(requestid.FromContext(c)) }, }, // Use the custom tag in your format string Format: "[${time}] ${ip} - ${requestid} - ${status} ${method} ${path}\n", })) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Listen(":3000") } ```
**Alternative: Manually Copying to `Locals`** If you have existing logging patterns that rely on `c.Locals` or prefer to manage these values in `Locals` for other reasons, you can manually copy the value from the context to `c.Locals` in a preceding middleware:
Example: Manually setting requestid in Locals ```go app.Use(requestid.New()) // Request ID middleware app.Use(func(c fiber.Ctx) error { // Manually copy the request ID to Locals c.Locals("requestid", requestid.FromContext(c)) return c.Next() }) app.Use(logger.New(logger.Config{ // Now ${locals:requestid} can be used, but CustomTags is generally preferred Format: "[${time}] ${ip} - ${locals:requestid} - ${status} ${method} ${path}\n", })) ```
Both approaches ensure your logger can access these values while respecting Go's context practices. The same template/tag mechanism is also available for application logs emitted through `log.WithContext`. This keeps request logging and handler logging consistent without hard-coding middleware-specific values into the `log` package: ```go app.Use(requestid.New()) // The requestid middleware automatically registers the ${requestid} tag. log.MustSetContextTemplate(log.ContextConfig{Format: "[${requestid}] "}) app.Get("/", func(c fiber.Ctx) error { // Pass c so middleware values stored on Fiber's request context can be read. log.WithContext(c).Info("handling request") return c.SendString("OK") }) ``` `SetContextTemplate` configures Fiber's built-in default logger. Custom loggers registered with `log.SetLogger` keep control over their own `WithContext` behavior. #### Breaking change: `WithContext(ctx any)` The signature of `log.WithContext`, `baseLogger.WithContext`, and `AllLogger[T].WithContext` changed from `WithContext(ctx context.Context)` to `WithContext(ctx any)`. The wider parameter type is what allows the same call site to receive `fiber.Ctx`, `*fasthttp.RequestCtx`, or a standard `context.Context` and still resolve middleware-stored values via `Value` / `UserValue`. Adapter packages that implement `AllLogger[T]` directly (for example community Zap, Zerolog, or Logrus integrations) must update their `WithContext` method signature accordingly. Direct call sites that already pass a `context.Context` or a `fiber.Ctx` continue to compile. #### New API surface The `log` and `middleware/logger` packages now expose the following exported symbols: - `log.SetContextTemplate(ContextConfig) error` / `log.MustSetContextTemplate(ContextConfig)` — configure the active context template. - `log.RegisterContextTag(name string, fn ContextTagFunc) error` / `log.MustRegisterContextTag` — register a global context tag. - `log.ContextConfig`, `log.ContextData`, `log.ContextTagFunc`, `log.Buffer` — supporting types. - `log.DefaultFormat`, `log.RequestIDFormat`, `log.KeyValueFormat`, `log.TagContextValue` — built-in format and tag constants. - `logger.RegisterTag(name string, fn LogFunc) error` / `logger.MustRegisterTag` — register a global access-log tag (in addition to per-instance `Config.CustomTags`). - `logger.RegisterContextTag(name string, extract func(ctx any) string)` — convenience helper that registers a string-valued tag in **both** the access-log registry and the `log.RegisterContextTag` registry, so middleware authors do not have to maintain two parallel renderers. - `logger.ErrUnknownTag` (sentinel) and `logger.UnknownTagError` (typed) — replace the older `ErrTemplateParameterMissing` sentinel that was never exported in a stable release. `New(Config{})` panics with `*UnknownTagError` when the format references a tag that has no registered renderer; `errors.As` retrieves the offending tag name. The `requestid`, `basicauth`, `keyauth`, `csrf`, and `session` middlewares automatically register their respective context tags (`${requestid}`, `${request-id}`, `${username}`, `${api-key}`, `${csrf-token}`, `${session-id}`) on first `New(...)`. Empty stubs for the same names are pre-registered at package init, so a logger format that references one of them compiles even when the corresponding middleware has not been initialized. For `log.WithContext`, later registration rebuilds the active context template. For `middleware/logger` access logs, construct the producing middleware (or call `logger.RegisterTag`) before `logger.New(...)`; existing logger instances keep the function chain compiled at construction time and do not retroactively pick up later registrations. The `Skip` is a function to determine if logging is skipped or written to `Stream`.
Example Usage ```go app.Use(logger.New(logger.Config{ Skip: func(c fiber.Ctx) bool { // Skip logging HTTP 200 requests return c.Response().StatusCode() == fiber.StatusOK }, })) ``` ```go app.Use(logger.New(logger.Config{ Skip: func(c fiber.Ctx) bool { // Only log errors, similar to an error.log return c.Response().StatusCode() < 400 }, })) ```
#### Predefined Formats Logger provides predefined formats that you can use by name or directly by specifying the format string.
Example Usage ```go app.Use(logger.New(logger.Config{ Format: logger.FormatCombined, })) ``` See more in [Logger](./middleware/logger.md#predefined-formats)
### Limiter The limiter middleware uses a new Fixed Window Rate Limiter implementation. Custom limiter algorithms should now implement the updated `limiter.Handler` interface, whose `New` method receives a pointer to the active config: `New(cfg *limiter.Config) fiber.Handler`. Limiter now redacts request keys in error paths by default. A new `DisableValueRedaction` boolean (default `false`) lets you reveal the raw limiter key if diagnostics require it. :::note Deprecated fields `Duration`, `Store`, and `Key` have been removed in v3. Use `Expiration`, `Storage`, and `KeyGenerator` instead. ::: ### Monitor Monitor middleware is migrated to the [Contrib package](https://github.com/gofiber/contrib/tree/main/monitor) with [PR #1172](https://github.com/gofiber/contrib/pull/1172). ### Proxy The proxy middleware has been updated to improve consistency with Go naming conventions. The `TlsConfig` field in the configuration struct has been renamed to `TLSConfig`. Additionally, the `WithTlsConfig` method has been removed; you should now configure TLS directly via the `TLSConfig` property within the `Config` struct. The new `KeepConnectionHeader` option (default `false`) drops the `Connection` header unless explicitly enabled to retain it. `proxy.Balancer` now accepts an optional variadic configuration: call `proxy.Balancer()` to use defaults or continue passing a `proxy.Config` value as before. ### Recover The Recover middleware allows customizing the error it returns. Set a `PanicHandler` in its `Config` to change the default behavior. ### Session The Session middleware has undergone key changes in v3 to improve functionality and flexibility. While v2 methods remain available for backward compatibility, we now recommend using the new middleware handler for session management. #### Key Updates The session middleware has undergone significant improvements in v3, focusing on type safety, flexibility, and better developer experience. #### Key Changes - **Extractor Pattern**: The string-based `KeyLookup` configuration has been replaced with a more flexible and type-safe `Extractor` function pattern. - **New Middleware Handler**: The `New` function now returns a middleware handler instead of a `*Store`. To access the session store, use the `Store` method on the middleware, or opt for `NewStore` or `NewWithStore` for custom store integration. - **Manual Session Release**: Session instances are no longer automatically released after being saved. To ensure proper lifecycle management, you must manually call `sess.Release()`. - **Idle Timeout**: The `Expiration` field has been replaced with `IdleTimeout`, which handles session inactivity. If the session is idle for the specified duration, it will expire. The idle timeout is updated when the session is saved. If you are using the middleware handler, the idle timeout will be updated automatically. - **Absolute Timeout**: The `AbsoluteTimeout` field has been added. If you need to set an absolute session timeout, you can use this field to define the duration. The session will expire after the specified duration, regardless of activity. - **Default KeyGenerator**: Changed from `utils.UUIDv4` to `utils.SecureToken`, producing base64-encoded tokens instead of UUID format. - **Context-Aware Lifecycle Methods**: The `DestroyWithContext`, `RegenerateWithContext`, `ResetWithContext`, and `SaveWithContext` methods (on both `Session` and `Middleware`) accept a `context.Context` to propagate cancellation and deadlines to the underlying storage I/O, mirroring the existing `Storage` and `SharedState` `WithContext` convention. The non-context variants delegate to these. A nil context is treated as `context.Background()`. For more details on these changes and migration instructions, check the [Session Middleware Migration Guide](./middleware/session.md#migration-guide). ### SSE Fiber now includes an [SSE middleware](./middleware/sse.md) for Server-Sent Events. It handles native `SendStreamWriter` setup, SSE response headers, event formatting, flushing, heartbeat comments, and disconnect detection through flush errors while leaving application-level hubs, topics, replay stores, and pub/sub bridges to user code or recipes. ### Timeout The timeout middleware is now configurable. A new `Config` struct allows customizing the timeout duration, defining a handler that runs when a timeout occurs, and specifying errors to treat as timeouts. The `New` function now accepts a `Config` value instead of a duration. **Behavioral changes:** - **Immediate return on timeout**: The middleware now returns immediately when a timeout occurs, without waiting for the handler to finish. This is achieved through the new **Abandon mechanism** which marks the context as abandoned so it won't be returned to the pool while the handler is still running. - **Context propagation**: The timeout context is properly propagated to the handler. Handlers can detect timeouts by listening on `c.Context().Done()` and return early. - **Panic handling**: Panics in the handler are caught and converted to `500 Internal Server Error` responses. - **Race-free design**: The implementation uses fasthttp's `TimeoutErrorWithCode` combined with Fiber's Abandon mechanism to ensure complete race-freedom between the middleware, handler goroutine, and context pooling. **New Ctx methods for the Abandon mechanism:** - `Abandon()`: Marks the context as abandoned - `IsAbandoned()`: Returns true if the context was abandoned - `ForceRelease()`: Releases an abandoned context back to the pool (for advanced use) **Migration:** Replace calls like `timeout.New(handler, 2*time.Second)` with `timeout.New(handler, timeout.Config{Timeout: 2 * time.Second})`. ## 🔌 Addons In v3, Fiber introduced Addons. Addons are additional useful packages that can be used in Fiber. ### Retry The Retry addon is a new addon that implements a retry mechanism for unsuccessful network operations. It 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.
Example ```go 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()) } ```
## 📋 Migration guide To streamline upgrades between Fiber versions, the Fiber CLI ships with a `migrate` command: ```bash go install github.com/gofiber/cli/fiber@latest fiber migrate --to v3 ``` ### Options - `-t, --to string` migrate to a specific version, e.g. `v3.0.0` - `-f, --force` force migration even if already on that version - `-s, --skip_go_mod` skip running `go mod tidy`, `go mod download`, and `go mod vendor` ### Changes Overview - [🚀 App](#-app-1) - [🎣 Hooks](#-hooks-1) - [🚀 Listen](#-listen-1) - [🗺 Router](#-router-1) - [🧠 Context](#-context-1) - [📎 Binding (was Parser)](#-parser) - [🔄 Redirect](#-redirect-1) - [🧾 Log](#-log-1) - [🌎 Client package](#-client-package-1) - [🛠️ Utils](#utils-migration) - [🧬 Middlewares](#-middlewares-1) - [Important Change for Accessing Middleware Data](#important-change-for-accessing-middleware-data) - [BasicAuth](#basicauth-1) - [Cache](#cache-1) - [CORS](#cors-1) - [CSRF](#csrf-1) - [Filesystem](#filesystem-1) - [EnvVar](#envvar-1) - [Favicon](#favicon) - [Healthcheck](#healthcheck-1) - [Monitor](#monitor-1) - [Proxy](#proxy-1) - [Session](#session-1) ### 🚀 App #### Static Since we've removed `app.Static()`, you need to move methods to static middleware like the example below: ```go // Before app.Static("/", "./public") app.Static("/prefix", "./public") app.Static("/prefix", "./public", Static{ Index: "index.htm", }) app.Static("*", "./public/index.html") ``` ```go // After app.Get("/*", static.New("./public")) app.Get("/prefix*", static.New("./public")) app.Get("/prefix*", static.New("./public", static.Config{ IndexNames: []string{"index.htm", "index.html"}, })) app.Get("*", static.New("./public/index.html")) ``` :::caution You have to put `*` to the end of the route if you don't define static route with `app.Use`. ::: #### Trusted Proxies We've renamed `EnableTrustedProxyCheck` to `TrustProxy` and moved `TrustedProxies` to `TrustProxyConfig`. **Important:** To use proxy headers like `X-Forwarded-For` with `c.IP()`, you must configure **all** of `TrustProxy`, `ProxyHeader`, and a trusted proxy via `TrustProxyConfig`. If the proxy is not trusted (for example, if you set only `ProxyHeader` or only `TrustProxy` without configuring `TrustProxyConfig`), proxy headers are ignored and `c.IP()` will return the remote TCP IP instead. ```go // Before app := fiber.New(fiber.Config{ // EnableTrustedProxyCheck enables the trusted proxy check. EnableTrustedProxyCheck: true, // TrustedProxies is a list of trusted proxy IP ranges/addresses. TrustedProxies: []string{"0.8.0.0", "127.0.0.0/8", "::1/128"}, }) ``` ```go // After app := fiber.New(fiber.Config{ // TrustProxy enables the trusted proxy check TrustProxy: true, // ProxyHeader specifies which header to read the real client IP from ProxyHeader: fiber.HeaderXForwardedFor, // TrustProxyConfig allows for configuring trusted proxies. TrustProxyConfig: fiber.TrustProxyConfig{ // Proxies is a list of trusted proxy IP ranges/addresses. Proxies: []string{"0.8.0.0"}, // Trust all loop-back IP addresses (127.0.0.0/8, ::1/128) Loopback: true, // Trust Unix domain socket connections UnixSocket: true, }, }) ``` For detailed proxy configuration guidance, see the [reverse proxy guide](./guide/reverse-proxy.md). ### 🎣 Hooks `OnShutdown` has been replaced by two hooks: `OnPreShutdown` and `OnPostShutdown`. Use them to run cleanup code before and after the server shuts down. When handling shutdown errors, register an `OnPostShutdown` hook and call `app.Listen()` in a goroutine. ```go // Before app.OnShutdown(func() { // Code to run before shutdown }) ``` ```go // After app.Hooks().OnPreShutdown(func() error { // Code to run before shutdown return nil }) ``` ### 🚀 Listen The `Listen` helpers (`ListenTLS`, `ListenMutualTLS`, etc.) were removed. Use `app.Listen()` with `fiber.ListenConfig` and a `tls.Config` when TLS is required. Options such as `ListenerNetwork` and `UnixSocketFileMode` are now configured via this struct. Prefer `TLSConfig` when you need full control, or use `CertFile` and `CertKeyFile` for quick TLS setup. ```go // Before app.ListenTLS(":3000", "cert.pem", "key.pem") ``` ```go // After app.Listen(":3000", fiber.ListenConfig{ CertFile: "./cert.pem", CertKeyFile: "./key.pem", }) ``` ### 🗺 Router #### Direct `net/http` handlers Route registration helpers now accept native `net/http` handlers. Pass an `http.Handler`, `http.HandlerFunc`, or compatible function directly to methods such as `app.Get`, `Group`, or `RouteChain` and Fiber will adapt it at registration time. Manual wrapping through the adaptor middleware is no longer required for these common cases. :::note Compatibility considerations Adapted handlers stick to `net/http` semantics. They do not interact with `fiber.Ctx` and are slower than native Fiber handlers because of the extra conversion layer. Use them to ease migrations, but prefer Fiber handlers in performance-critical paths. ::: ```go httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if _, err := w.Write([]byte("served by net/http")); err != nil { panic(err) } }) app.Get("/", httpHandler) ``` #### Middleware Registration The signatures for [`Add`](#middleware-registration) and [`Route`](#route-chaining) have been changed. To migrate [`Add`](#middleware-registration) you must change the `methods` in a slice. ```go // Before app.Add(fiber.MethodPost, "/api", myHandler) ``` ```go // After app.Add([]string{fiber.MethodPost}, "/api", myHandler) ``` #### Mounting In this release, the `Mount` method has been removed. Instead, you can use the `Use` method to achieve similar functionality. ```go // Before app.Mount("/api", apiApp) ``` ```go // After app.Use("/api", apiApp) ``` #### Route Chaining Refer to the [route chaining](#route-chaining) section for details on the new `RouteChain` helper. The `Route` function now matches its v2 behavior for prefix encapsulation. ```go // Before app.Route("/api", func(apiGrp Router) { apiGrp.Route("/user/:id?", func(userGrp Router) { userGrp.Get("/", func(c fiber.Ctx) error { // Get user return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) }) userGrp.Post("/", func(c fiber.Ctx) error { // Create user return c.JSON(fiber.Map{"message": "User created"}) }) }) }) ``` ```go // After app.RouteChain("/api").RouteChain("/user/:id?"). Get(func(c fiber.Ctx) error { // Get user return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) }). Post(func(c fiber.Ctx) error { // Create user return c.JSON(fiber.Map{"message": "User created"}) }) ``` ### 🗺 RebuildTree We introduced a new method that enables rebuilding the route tree stack at runtime. This allows you to add routes dynamically while your application is running and update the route tree to make the new routes available for use. For more details, refer to the [app documentation](./api/app.md#rebuildtree): #### Example Usage ```go app.Get("/define", func(c fiber.Ctx) error { // Define a new route dynamically app.Get("/dynamically-defined", func(c fiber.Ctx) error { // Adding a dynamically defined route return c.SendStatus(http.StatusOK) }) app.RebuildTree() // Rebuild the route tree to register the new route return c.SendStatus(http.StatusOK) }) ``` In this example, a new route is defined, and `RebuildTree()` is called to ensure the new route is registered and available. Note: Use this method with caution. It is **not** thread-safe and can be very performance-intensive. Therefore, it should be used sparingly and primarily in development mode. It should not be invoke concurrently. #### RemoveRoute - **RemoveRoute**: Removes route by path - **RemoveRouteByName**: Removes route by name - **RemoveRouteFunc**: Removes route by a function having `*Route` parameter For more details, refer to the [app documentation](./api/app.md#removeroute): ### 🧠 Context Fiber v3 introduces several new features and changes to the Ctx interface, enhancing its functionality and flexibility. - **ParamsInt**: Use `Params` with generic types. - **QueryBool**: Use `Query` with generic types. - **QueryFloat**: Use `Query` with generic types. - **QueryInt**: Use `Query` with generic types. - **Bind**: Now used for binding instead of view binding. Use `c.ViewBind()` for view binding. In Fiber v3, the `Ctx` parameter in handlers is now an interface, which means the `*` symbol is no longer used. Here is an example demonstrating this change:
Example **Before**: ```go package main import ( "github.com/gofiber/fiber/v2" ) func main() { app := fiber.New() // Route Handler with *fiber.Ctx app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Listen(":3000") } ``` **After**: ```go package main import ( "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Route Handler without *fiber.Ctx app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) app.Listen(":3000") } ``` **Explanation**: In this example, the `Ctx` parameter in the handler is used as an interface (`fiber.Ctx`) instead of a pointer (`*fiber.Ctx`). This change allows for more flexibility and customization in Fiber v3.
#### 📎 Parser The `Parser` section in Fiber v3 has undergone significant changes to improve functionality and flexibility. ##### Migration Instructions 1. **BodyParser**: Use `c.Bind().Body()` instead of `c.BodyParser()`.
Example ```go // Before app.Post("/user", func(c *fiber.Ctx) error { var user User if err := c.BodyParser(&user); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(user) }) ``` ```go // After app.Post("/user", func(c fiber.Ctx) error { var user User if err := c.Bind().Body(&user); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(user) }) ```
2. **ParamsParser**: Use `c.Bind().URI()` instead of `c.ParamsParser()`. Note that the struct tag has changed from `params` to `uri`.
Example ```go // Before type Params struct { ID int `params:"id"` } app.Get("/user/:id", func(c *fiber.Ctx) error { var params Params if err := c.ParamsParser(¶ms); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(params) }) ``` ```go // After type Params struct { ID int `uri:"id"` } app.Get("/user/:id", func(c fiber.Ctx) error { var params Params if err := c.Bind().URI(¶ms); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(params) }) ```
3. **QueryParser**: Use `c.Bind().Query()` instead of `c.QueryParser()`.
Example ```go // Before app.Get("/search", func(c *fiber.Ctx) error { var query Query if err := c.QueryParser(&query); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(query) }) ``` ```go // After app.Get("/search", func(c fiber.Ctx) error { var query Query if err := c.Bind().Query(&query); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(query) }) ```
4. **CookieParser**: Use `c.Bind().Cookie()` instead of `c.CookieParser()`.
Example ```go // Before app.Get("/cookie", func(c *fiber.Ctx) error { var cookie Cookie if err := c.CookieParser(&cookie); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(cookie) }) ``` ```go // After app.Get("/cookie", func(c fiber.Ctx) error { var cookie Cookie if err := c.Bind().Cookie(&cookie); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(cookie) }) ```
#### 🔄 Redirect Fiber v3 enhances the redirect functionality by introducing new methods and improving existing ones. The new redirect methods provide more flexibility and control over the redirection process. ##### Migration Instructions 1. **RedirectToRoute**: Use `c.Redirect().Route()` instead of `c.RedirectToRoute()`.
Example ```go // Before app.Get("/old", func(c *fiber.Ctx) error { return c.RedirectToRoute("newRoute") }) ``` ```go // After app.Get("/old", func(c fiber.Ctx) error { return c.Redirect().Route("newRoute") }) ```
2. **RedirectBack**: Use `c.Redirect().Back()` instead of `c.RedirectBack()`.
Example ```go // Before app.Get("/back", func(c *fiber.Ctx) error { return c.RedirectBack() }) ``` ```go // After app.Get("/back", func(c fiber.Ctx) error { return c.Redirect().Back() }) ```
3. **Redirect**: Use `c.Redirect().To()` instead of `c.Redirect()`.
Example ```go // Before app.Get("/old", func(c *fiber.Ctx) error { return c.Redirect("/new") }) ``` ```go // After app.Get("/old", func(c fiber.Ctx) error { return c.Redirect().To("/new") }) ```
#### 🧾 Log The `ConfigurableLogger` and `AllLogger` interfaces now use generics. You can specify the underlying logger type when implementing these interfaces. While `any` can be used for maximum flexibility in some contexts, when retrieving the concrete logger via `log.DefaultLogger`, you must specify the exact underlying logger type, for example `log.DefaultLogger[*MyLogger]().Logger()`. ### 🌎 Client package Fiber v3 introduces a completely rebuilt client package with numerous new features such as Cookiejar, request/response hooks, and more. Here is a guide to help you migrate from Fiber v2 to Fiber v3. #### New Features - **Cookiejar**: Manage cookies automatically. - **Request/Response Hooks**: Customize request and response handling. - **Improved Error Handling**: Better error management and reporting. #### Migration Instructions **Import Path**: Update the import path to the new client package.
Before ```go import "github.com/gofiber/fiber/v2/client" ```
After ```go import "github.com/gofiber/fiber/v3/client" ```
**Common migrations**: 1. **Shared defaults instead of per-call mutation**: Move headers and timeouts into the reusable client and override with `client.Config` when needed.
Example ```go // Before status, body, errs := fiber.Get("https://api.example.com/users"). Set("Authorization", "Bearer "+token). Timeout(5 * time.Second). String() if len(errs) > 0 { return fmt.Errorf("request failed: %v", errs) } fmt.Println(status, body) ``` ```go // After cli := client.New(). AddHeader("Authorization", "Bearer "+token). SetTimeout(5 * time.Second) resp, err := cli.Get("https://api.example.com/users") if err != nil { return err } defer resp.Close() fmt.Println(resp.StatusCode(), resp.String()) ```
2. **Body handling**: Replace `Agent.JSON(...).Struct(&dst)` with request bodies through `client.Config` (or `Request.SetJSON`) and decode the response via `Response.JSON`.
Example ```go // Before var created user status, _, errs := fiber.Post("https://api.example.com/users"). JSON(payload). Struct(&created) if len(errs) > 0 { return fmt.Errorf("request failed: %v", errs) } fmt.Println(status, created) ``` ```go // After cli := client.New() resp, err := cli.Post("https://api.example.com/users", client.Config{ Body: payload, }) if err != nil { return err } defer resp.Close() var created user if err := resp.JSON(&created); err != nil { return fmt.Errorf("decode failed: %w", err) } fmt.Println(resp.StatusCode(), created) ```
3. **Path and query parameters**: Use the new path/query helpers instead of manually formatting URLs.
Example ```go // Before code, body, errs := fiber.Get(fmt.Sprintf("https://api.example.com/users/%s", id)). QueryString("active=true"). String() if len(errs) > 0 { return fmt.Errorf("request failed: %v", errs) } fmt.Println(code, body) ``` ```go // After cli := client.New().SetBaseURL("https://api.example.com") resp, err := cli.Get("/users/:id", client.Config{ PathParam: map[string]string{"id": id}, Param: map[string]string{"active": "true"}, }) if err != nil { return err } defer resp.Close() fmt.Println(resp.StatusCode(), resp.String()) ```
4. **Agent helpers**: `Agent.Bytes`, `AcquireAgent`, and `Agent.Parse` have been removed. Reuse a `client.Client` instance (or pool requests/responses directly) and access response data through the new typed helpers.
Example ```go // Before agent := fiber.AcquireAgent() status, body, errs := agent.Get("https://api.example.com/users").Bytes() fiber.ReleaseAgent(agent) if len(errs) > 0 { return fmt.Errorf("request failed: %v", errs) } var users []user if err := fiber.Parse(body, &users); err != nil { return fmt.Errorf("parse failed: %w", err) } fmt.Println(status, len(users)) ``` ```go // After cli := client.New() resp, err := cli.Get("https://api.example.com/users") if err != nil { return err } defer resp.Close() var users []user if err := resp.JSON(&users); err != nil { return fmt.Errorf("decode failed: %w", err) } fmt.Println(resp.StatusCode(), len(users)) ``` :::tip If you need pooling, use `client.AcquireRequest`, `client.AcquireResponse`, and their corresponding release functions around a long-lived `client.Client` instead of the removed agent pool. :::
5. **Fiber-level shortcuts**: The `fiber.Get`, `fiber.Post`, and similar top-level helpers are no longer exposed from the main module. Use the client package equivalents (`client.Get`, `client.Post`, etc.) which call the shared default client (or pass your own client instance for custom defaults).
Example ```go // Before status, body, errs := fiber.Get("https://api.example.com/health").String() if len(errs) > 0 { return fmt.Errorf("request failed: %v", errs) } fmt.Println(status, body) ``` ```go // After resp, err := client.Get("https://api.example.com/health") if err != nil { return err } defer resp.Close() fmt.Println(resp.StatusCode(), resp.String()) ``` :::note The `client.Get`/`client.Post` helpers use `client.C()` (the default shared client). For custom defaults, construct a client with `client.New()` and invoke its methods instead. :::
#### Complete API Migration Reference
Click to expand full v2 → v3 API mapping tables ##### Core Concepts | Description | v2 | v3 | |-------------|----|----| | Import | `github.com/gofiber/fiber/v2` | `github.com/gofiber/fiber/v3/client` | | Client Concept | `*fiber.Agent` | `*client.Client` + `*client.Request` | | Response Concept | `(code int, body []byte, errs []error)` | `(*client.Response, error)` | ##### Client/Agent Creation | Description | v2 | v3 | |-------------|----|----| | Create Agent/Client | `fiber.AcquireAgent()` | `client.New()` | | Get from pool | `fiber.AcquireAgent()` | `client.AcquireRequest()` | | Release | `fiber.ReleaseAgent(a)` | `client.ReleaseRequest(req)` | | With fasthttp.Client | - | `client.NewWithClient(c)` | | With HostClient | - | `client.NewWithHostClient(hc)` | | With LBClient | - | `client.NewWithLBClient(lb)` | | Get Request object | `a.Request()` | `c.R()` | | Default client | - | `client.C()` | | Replace default | - | `client.Replace(c)` | ##### HTTP Methods | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | GET | `fiber.Get(url)` | `c.Get(url, cfg...)` | `req.Get(url)` | | POST | `fiber.Post(url)` | `c.Post(url, cfg...)` | `req.Post(url)` | | PUT | `fiber.Put(url)` | `c.Put(url, cfg...)` | `req.Put(url)` | | PATCH | `fiber.Patch(url)` | `c.Patch(url, cfg...)` | `req.Patch(url)` | | DELETE | `fiber.Delete(url)` | `c.Delete(url, cfg...)` | `req.Delete(url)` | | HEAD | `fiber.Head(url)` | `c.Head(url, cfg...)` | `req.Head(url)` | | OPTIONS | - | `c.Options(url, cfg...)` | `req.Options(url)` | | Custom | - | `c.Custom(url, method, cfg...)` | `req.Custom(url, method)` | ##### URL & Method | Description | v2 | v3 | |-------------|----|----| | Set URL | `req.SetRequestURI(url)` | `req.SetURL(url)` | | Get URL | `req.URI().String()` | `req.URL()` | | Set Method | `req.Header.SetMethod(method)` | `req.SetMethod(method)` | | Set Base URL | - | `c.SetBaseURL(url)` | ##### Request Execution & Response | Description | v2 | v3 | |-------------|----|----| | Parse Request | `a.Parse()` | Not needed | | Execute (bytes) | `a.Bytes()` → `(code, body, errs)` | `req.Send()` → `(*Response, error)` | | Execute (string) | `a.String()` | `resp.String()` | | Execute (struct) | `a.Struct(&v)` | `resp.JSON(&v)` / `resp.XML(&v)` | | Status Code | Return value `code` | `resp.StatusCode()` | | Status Text | - | `resp.Status()` | | Body (bytes) | Return value `body` | `resp.Body()` | | Response Header | `resp.Header.Peek(key)` | `resp.Header(key)` | | All Headers | `resp.Header.VisitAll(fn)` | `resp.Headers()` | | Cookies | - | `resp.Cookies()` | | Save to file | - | `resp.Save(path)` | | Close | - | `resp.Close()` | ##### Headers | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | Set Header | `a.Set(k, v)` | `c.SetHeader(k, v)` | `req.SetHeader(k, v)` | | Add Header | `a.Add(k, v)` | `c.AddHeader(k, v)` | `req.AddHeader(k, v)` | | Multiple Headers | - | `c.SetHeaders(map)` | `req.SetHeaders(map)` | | Bytes variants | `a.SetBytesK/V/KV()` | - | - | ##### User-Agent, Referer, Content-Type, Host | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | User-Agent | `a.UserAgent(ua)` | `c.SetUserAgent(ua)` | `req.SetUserAgent(ua)` | | Referer | `a.Referer(ref)` | `c.SetReferer(ref)` | `req.SetReferer(ref)` | | Content-Type | `a.ContentType(ct)` | - | `req.SetHeader("Content-Type", ct)` | | Host | `a.Host(host)` | - | `req.SetHeader("Host", host)` | | Connection Close | `a.ConnectionClose()` | - | `req.SetHeader("Connection", "close")` | ##### Cookies | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | Set Cookie | `a.Cookie(k, v)` | `c.SetCookie(k, v)` | `req.SetCookie(k, v)` | | Multiple | `a.Cookies(k1, v1, ...)` | `c.SetCookies(map)` | `req.SetCookies(map)` | | With Struct | - | `c.SetCookiesWithStruct(v)` | `req.SetCookiesWithStruct(v)` | | Cookie Jar | - | `c.SetCookieJar(jar)` | - | :::caution Cookie jar path scoping `CookieJar.Set(uri, cookies...)` scopes a cookie that carries no usable `Path` attribute to the request URI's [RFC 6265 §5.1.4](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4) default-path, matching how a `Set-Cookie` received for that URI is stored. A cookie set against `/a/b` with no `Path` is scoped to `/a`, not to the whole host, so it is no longer sent to `/other`. Set `Path` explicitly to keep a cookie host-wide: ```go c := fasthttp.AcquireCookie() c.SetKey("session") c.SetValue(token) c.SetPath("/") // without this, the scope comes from uri's directory jar.Set(uri, c) ``` `SetByHost`, `SetKeyValue` and `SetKeyValueBytes` take no URI, so they have no request path to derive a scope from and continue to store such cookies at `/`. ::: ##### Query Parameters | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | Query String | `a.QueryString(qs)` | - | - | | Add Param | - | `c.AddParam(k, v)` | `req.AddParam(k, v)` | | Set Param | - | `c.SetParam(k, v)` | `req.SetParam(k, v)` | | With Struct | - | `c.SetParamsWithStruct(v)` | `req.SetParamsWithStruct(v)` | ##### Path Parameters (NEW) | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | Set Path Param | - | `c.SetPathParam(k, v)` | `req.SetPathParam(k, v)` | | Multiple | - | `c.SetPathParams(map)` | `req.SetPathParams(map)` | | With Struct | - | `c.SetPathParamsWithStruct(v)` | `req.SetPathParamsWithStruct(v)` | ##### Request Body | Description | v2 | v3 | |-------------|----|----| | Body (bytes) | `a.Body(body)` | `req.SetRawBody(body)` | | Body (string) | `a.BodyString(body)` | `req.SetRawBody([]byte(body))` | | Body Stream | `a.BodyStream(r, size)` | - | | JSON | `a.JSON(v)` | `req.SetJSON(v)` | | XML | `a.XML(v)` | `req.SetXML(v)` | | CBOR (NEW) | - | `req.SetCBOR(v)` | ##### Form Data | Description | v2 | v3 | |-------------|----|----| | Create Args | `fiber.AcquireArgs()` | Direct on Request | | Send Form | `a.Form(args)` | `req.SetFormData(k, v)` | | Add Form Data | `args.Set(k, v)` | `req.AddFormData(k, v)` | | With Map | - | `req.SetFormDataWithMap(map)` | | With Struct | - | `req.SetFormDataWithStruct(v)` | ##### File Upload | Description | v2 | v3 | |-------------|----|----| | Multipart Form | `a.MultipartForm(args)` | Automatic | | Boundary | `a.Boundary(b)` | `req.SetBoundary(b)` | | Send File | `a.SendFile(f, field...)` | `req.AddFile(path)` | | Multiple Files | `a.SendFiles(...)` | `req.AddFiles(files...)` | | With Reader | - | `req.AddFileWithReader(name, r)` | | FileData | `a.FileData(files...)` | `req.AddFiles(files...)` | ##### Timeout & TLS | Description | v2 | v3 (Client) | v3 (Request) | |-------------|----|----|--------------| | Timeout | `a.Timeout(d)` | `c.SetTimeout(d)` | `req.SetTimeout(d)` | | Max Redirects | `a.MaxRedirectsCount(n)` | Via Config | `req.SetMaxRedirects(n)` | | TLS Config | `a.TLSConfig(cfg)` | `c.SetTLSConfig(cfg)` | - | | Skip Verify | `a.InsecureSkipVerify()` | Via `tls.Config` | - | | Certificates | - | `c.SetCertificates(...)` | - | | Root Cert | - | `c.SetRootCertificate(path)` | - | ##### JSON/XML Encoder | Description | v2 | v3 | |-------------|----|----| | JSON Encoder | `a.JSONEncoder(fn)` | `c.SetJSONMarshal(fn)` | | JSON Decoder | `a.JSONDecoder(fn)` | `c.SetJSONUnmarshal(fn)` | | XML Encoder | - | `c.SetXMLMarshal(fn)` | | XML Decoder | - | `c.SetXMLUnmarshal(fn)` | | CBOR (NEW) | - | `c.SetCBORMarshal/Unmarshal(fn)` | ##### Authentication | Description | v2 | v3 | |-------------|----|----| | Basic Auth | `a.BasicAuth(user, pass)` | Via Header (Base64) | ##### Debug & Retry | Description | v2 | v3 | |-------------|----|----| | Debug | `a.Debug(w...)` | `c.Debug()` | | Disable Debug | - | `c.DisableDebug()` | | Logger | - | `c.SetLogger(logger)` | | Retry | `a.RetryIf(fn)` | `c.SetRetryConfig(cfg)` | ##### Reuse & Reset | Description | v2 | v3 | |-------------|----|----| | Reuse Agent | `a.Reuse()` | Use pool | | Reset Client | - | `c.Reset()` | | Dest Buffer | `a.Dest(dest)` | - | ##### NEW in v3 | Feature | v3 API | |---------|--------| | Request Hooks | `c.AddRequestHook(fn)` | | Response Hooks | `c.AddResponseHook(fn)` | | Proxy | `c.SetProxyURL(url)` | | Context | `req.SetContext(ctx)` | | Dial Function | `c.SetDial(fn)` | | Raw Request | `req.RawRequest` | | Raw Response | `resp.RawResponse` | ##### Key Differences 1. **Architecture**: v2 `Agent` → v3 separate `Client`, `Request`, `Response` 2. **Error Handling**: v2 `[]error` → v3 single `error` 3. **Response**: v2 tuple `(code, body, errs)` → v3 `*Response` object 4. **No Parse()**: v3 auto-initializes requests 5. **Hooks**: v3 adds request/response middleware 6. **Path Params**: v3 native `:param` support 7. **Cookie Jar**: v3 built-in session management 8. **CBOR**: v3 adds CBOR encoding 9. **Context**: v3 native cancellation support 10. **Iterators**: v3 uses `iter.Seq2` for collections 11. **Bytes variants removed**: v2 `*Bytes*` methods gone
### 🛠️ Utils {#utils-migration} Fiber v3 removes the in-repo `utils` package in favor of the external [`github.com/gofiber/utils/v2`](https://github.com/gofiber/utils) module. 1. Replace imports: ```go - import "github.com/gofiber/fiber/v2/utils" + import "github.com/gofiber/utils/v2" ``` 1. Review function changes: | v2 function | v3 replacement | | --- | --- | | `AssertEqual` | removed; use testing libraries like [`github.com/stretchr/testify/assert`](https://pkg.go.dev/github.com/stretchr/testify/assert) | | `ToLowerBytes` | `utils.ToLowerBytes` | | `ToUpperBytes` | `utils.ToUpperBytes` | | `TrimRightBytes` | `utils.TrimRight` | | `TrimLeftBytes` | `utils.TrimLeft` | | `TrimBytes` | `utils.Trim` | | `EqualFoldBytes` | `utils.EqualFold` | | `UUID` | `utils.UUID` | | `UUIDv4` | `utils.UUIDv4` | | `FunctionName` | `utils.FunctionName` | | `GetArgument` | `utils.GetArgument` | | `IncrementIPRange` | `utils.IncrementIPRange` | | `ConvertToBytes` | `utils.ConvertToBytes` | | `CopyString` | `utils.CopyString` | | `CopyBytes` | `utils.CopyBytes` | | `ByteSize` | `utils.ByteSize` | | `ToString` | `utils.ToString` | | `UnsafeString` | `utils.UnsafeString` | | `UnsafeBytes` | `utils.UnsafeBytes` | | `GetString` | removed; use `utils.ToString` or the standard library | | `GetBytes` | removed; use `utils.CopyBytes` or `[]byte(s)` | | `ImmutableString` | removed; strings are already immutable | | `GetMIME` | `utils.GetMIME` | | `ParseVendorSpecificContentType` | `utils.ParseVendorSpecificContentType` | | `StatusMessage` | `utils.StatusMessage` | | `IsIPv4` | `utils.IsIPv4` | | `IsIPv6` | `utils.IsIPv6` | | `ToLower` | `utils.ToLower` | | `ToUpper` | `utils.ToUpper` | | `TrimLeft` | `strings.TrimLeft` | | `Trim` | `strings.Trim` | | `TrimRight` | `strings.TrimRight` | | `EqualFold` | `strings.EqualFold` | | `StartTimeStampUpdater` | `utils.StartTimeStampUpdater` (new `utils.Timestamp` provides the current value) | 1. Update your code. For example: ```go // v2 import oldutils "github.com/gofiber/fiber/v2/utils" func demo() { b := oldutils.TrimBytes([]byte(" fiber ")) id := oldutils.UUIDv4() s := oldutils.GetString([]byte("foo")) } // v3 import ( "github.com/gofiber/utils/v2" "strings" ) func demo() { s := utils.TrimSpace(" fiber ") id := utils.UUIDv4() str := utils.ToString([]byte("foo")) t := strings.TrimRight("bar ", " ") } ``` The `github.com/gofiber/utils/v2` module also introduces new helpers like `ParseInt`, `ParseUint`, `Walk`, `ReadFile`, and `Timestamp`. ### 🧬 Middlewares #### Important Change for Accessing Middleware Data **Change:** In Fiber v2, some middlewares set data in `c.Locals()` using string keys (e.g., `c.Locals("requestid")`). In Fiber v3, to align with Go's context best practices and prevent key collisions, these middlewares now store their specific data in the request's context using unexported keys of custom types. **Impact:** Directly accessing these middleware-provided values via `c.Locals("some_string_key")` will no longer work. **Migration Action:** The `ContextKey` configuration option has been removed from all middlewares. Values are no longer stored under user-defined keys. You must update your code to use the dedicated exported functions provided by each affected middleware to retrieve its data from the context. **Examples of new helper functions to use:** - `requestid.FromContext(c)` - `csrf.TokenFromContext(c)` - `csrf.HandlerFromContext(c)` - `session.FromContext(c)` - `basicauth.UsernameFromContext(c)` - `keyauth.TokenFromContext(c)` **For logging these values:** The recommended approach is to use the `CustomTags` feature of the Logger middleware, which allows you to call these specific `FromContext` functions. Refer to the [Logger section in "What's New"](#logger) for detailed examples. :::note If you were manually setting and retrieving your own application-specific values in `c.Locals()` using string keys, that functionality remains unchanged. This change specifically pertains to how Fiber's built-in (and some contrib) middlewares expose their data. ::: #### BasicAuth The `Authorizer` callback now receives the current request context. Update custom functions from: ```go Authorizer: func(user, pass string) bool { // v2 style return user == "admin" && pass == "secret" } ``` to: ```go Authorizer: func(user, pass string, _ fiber.Ctx) bool { // v3 style with access to the Fiber context return user == "admin" && pass == "secret" } ``` Passwords configured for BasicAuth must now be pre-hashed. If no prefix is supplied the middleware expects a SHA-256 digest encoded in hex. Common prefixes like `{SHA256}` and `{SHA512}` and bcrypt strings are also supported. Plaintext passwords are no longer accepted. Unauthorized responses also include a `Vary: Authorization` header for correct caching behavior. You can also set the optional `HeaderLimit` and `Charset` options to further control authentication behavior. #### KeyAuth The keyauth middleware was updated to introduce a configurable `Realm` field for the `WWW-Authenticate` header. The old string-based `KeyLookup` configuration has been replaced with an `Extractor` field, and the `AuthScheme` field has been removed. The auth scheme is now inferred from the extractor used (e.g., `keyauth.FromAuthHeader`). Use helper functions like `keyauth.FromHeader`, `keyauth.FromAuthHeader`, or `keyauth.FromCookie` to define where the key should be retrieved from. Multiple sources can be combined with `keyauth.Chain`. New `Challenge`, `Error`, `ErrorDescription`, `ErrorURI`, and `Scope` options let you customize challenge responses, include Bearer error parameters, and specify required scopes. `ErrorURI` values are validated as absolute, credentials containing whitespace are rejected, and when multiple authorization extractors are chained, all schemes are advertised in the `WWW-Authenticate` header. The middleware defers emitting `WWW-Authenticate` until a 401 status is final, and `FromAuthHeader` now trims surrounding whitespace. ```go // Before app.Use(keyauth.New(keyauth.Config{ KeyLookup: "header:Authorization", AuthScheme: "Bearer", Validator: validateAPIKey, })) // After app.Use(keyauth.New(keyauth.Config{ Extractor: keyauth.FromAuthHeader(fiber.HeaderAuthorization, "Bearer"), Validator: validateAPIKey, })) ``` Combine multiple sources with `keyauth.Chain()` when needed. #### Cache The deprecated `Store` and `Key` fields were removed. Use `Storage` and `KeyGenerator` instead to configure caching backends and cache keys. Defaults also changed: the middleware now emits `Cache-Control` headers, the default `Expiration` increased to `5 minutes` (from `1 minute`), and a new `MaxBytes` limit of `1 MB` (previously unlimited) now caps cached payloads. To restore v2 behavior: - Set `DisableCacheControl` to `true` to suppress automatic `Cache-Control` headers. - Configure `Expiration` to `1*time.Minute`. - Set `MaxBytes` to `0` (or a higher value) when caching large responses. - Disable structured key dimensions as needed (for example `DisableQueryKeys: true`), or provide a custom `KeyGenerator`. Additional v3 cache key options: - `Methods`: HTTP methods eligible for caching (default `GET`, `HEAD`) - `DisableQueryKeys`: disable canonicalized query args in keys (default `false`) - `KeyHeaders`: request header allow-list for key partitioning - `KeyCookies`: explicit cookie allow-list for key partitioning - `DisableVaryHeaders`: disable response `Vary` dimensions in lookup/storage partitioning (default `false`) #### CORS The CORS middleware has been updated to use slices instead of strings for the `AllowOrigins`, `AllowMethods`, `AllowHeaders`, and `ExposeHeaders` fields. Here's how you can update your code: ```go // Before app.Use(cors.New(cors.Config{ AllowOrigins: "https://example.com,https://example2.com", AllowMethods: strings.Join([]string{fiber.MethodGet, fiber.MethodPost}, ","), AllowHeaders: "Content-Type", ExposeHeaders: "Content-Length", })) // After app.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://example.com", "https://example2.com"}, AllowMethods: []string{fiber.MethodGet, fiber.MethodPost}, AllowHeaders: []string{"Content-Type"}, ExposeHeaders: []string{"Content-Length"}, })) ``` #### CSRF - **Field Renaming**: The `Expiration` field in the CSRF middleware configuration has been renamed to `IdleTimeout` to better describe its functionality. Additionally, the default value has been reduced from 1 hour to 30 minutes. Update your code as follows: ```go // Before app.Use(csrf.New(csrf.Config{ Expiration: 10 * time.Minute, })) // After app.Use(csrf.New(csrf.Config{ IdleTimeout: 10 * time.Minute, })) ``` - **Session Key Removal**: The `SessionKey` field has been removed from the CSRF middleware configuration. The session key is now an unexported constant within the middleware to avoid potential key collisions in the session store. - **KeyLookup Field Removal**: The `KeyLookup` field has been removed from the CSRF middleware configuration. This field was deprecated and is no longer needed as the middleware now uses a more secure approach for token management. - **DisableValueRedaction Toggle**: CSRF redacts tokens and storage keys by default; set `DisableValueRedaction` to `true` when diagnostics require the raw values. - **Default KeyGenerator**: Changed from `utils.UUIDv4` to `utils.SecureToken`, producing base64-encoded tokens instead of UUID format. ```go // Before app.Use(csrf.New(csrf.Config{ KeyLookup: "header:X-Csrf-Token", // other config... })) // After - use Extractor instead app.Use(csrf.New(csrf.Config{ Extractor: csrf.FromHeader("X-Csrf-Token"), // other config... })) ``` - **FromCookie Extractor Removal**: The `csrf.FromCookie` extractor has been intentionally removed for security reasons. Using cookie-based extraction defeats the purpose of CSRF protection by making the extracted token always match the cookie value. ```go // Before - This was a security vulnerability app.Use(csrf.New(csrf.Config{ Extractor: csrf.FromCookie("csrf_token"), // ❌ Insecure! })) // After - Use secure extractors instead app.Use(csrf.New(csrf.Config{ Extractor: csrf.FromHeader("X-Csrf-Token"), // ✅ Secure // or Extractor: csrf.FromForm("_csrf"), // ✅ Secure // or Extractor: csrf.FromQuery("csrf_token"), // ✅ Acceptable })) ``` **Security Note**: The removal of `FromCookie` prevents a common misconfiguration that would completely bypass CSRF protection. The middleware uses the Double Submit Cookie pattern, which requires the token to be submitted through a different channel than the cookie to provide meaningful protection. #### Idempotency - **DisableValueRedaction Toggle**: The idempotency middleware now hides keys in logs and error paths by default, with a `DisableValueRedaction` boolean (default `false`) to reveal them when needed. #### Timeout The timeout middleware now accepts a configuration struct instead of a duration. Update your code as follows: ```go // Before app.Use(timeout.New(handler, 2*time.Second)) // After app.Use(timeout.New(handler, timeout.Config{Timeout: 2 * time.Second})) ``` **Important behavioral changes:** - The middleware now returns immediately on timeout without waiting for the handler (using the new Abandon mechanism). - Handlers can detect timeouts by listening on `c.Context().Done()` and return early. - Panics in the handler are caught and converted to `500 Internal Server Error`. #### Filesystem You need to move filesystem middleware to static middleware due to it has been removed from the core. ```go // Before app.Use(filesystem.New(filesystem.Config{ Root: http.Dir("./assets"), })) app.Use(filesystem.New(filesystem.Config{ Root: http.Dir("./assets"), Browse: true, Index: "index.html", MaxAge: 3600, })) ``` ```go // After app.Use(static.New("", static.Config{ FS: os.DirFS("./assets"), })) app.Use(static.New("", static.Config{ FS: os.DirFS("./assets"), Browse: true, IndexNames: []string{"index.html"}, MaxAge: 3600, })) ``` #### EnvVar The `ExcludeVars` option has been removed. Remove any references to it and use `ExportVars` to explicitly list environment variables that should be exposed. #### Healthcheck Previously, the Healthcheck middleware was configured with a combined setup for liveness and readiness probes: ```go //before app.Use(healthcheck.New(healthcheck.Config{ LivenessProbe: func(c fiber.Ctx) bool { return true }, LivenessEndpoint: "/live", ReadinessProbe: func(c fiber.Ctx) bool { return serviceA.Ready() && serviceB.Ready() && ... }, ReadinessEndpoint: "/ready", })) ``` With the new version, each health check endpoint is configured separately, allowing for more flexibility: ```go // after // Default liveness endpoint configuration app.Get(healthcheck.LivenessEndpoint, healthcheck.New(healthcheck.Config{ Probe: func(c fiber.Ctx) bool { return true }, })) // Default readiness endpoint configuration app.Get(healthcheck.ReadinessEndpoint, healthcheck.New()) // New default startup endpoint configuration // Default endpoint is /startupz app.Get(healthcheck.StartupEndpoint, healthcheck.New(healthcheck.Config{ Probe: func(c fiber.Ctx) bool { return serviceA.Ready() && serviceB.Ready() && ... }, })) // Custom liveness endpoint configuration app.Get("/live", healthcheck.New()) ``` #### Monitor Since v3 the Monitor middleware has been moved to the [Contrib package](https://github.com/gofiber/contrib/tree/main/monitor) ```go // Before import "github.com/gofiber/fiber/v2/middleware/monitor" app.Use("/metrics", monitor.New()) ``` You only need to change the import path to the contrib package. ```go // After import "github.com/gofiber/contrib/monitor" app.Use("/metrics", monitor.New()) ``` #### Proxy In previous versions, TLS settings for the proxy middleware were set using the `WithTlsConfig` method. This method has been removed in favor of a more idiomatic configuration via the `TLSConfig` field in the `Config` struct. #### Before (v2 usage) ```go proxy.WithTlsConfig(&tls.Config{ InsecureSkipVerify: true, }) // Forward to url app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif")) ``` #### After (v3 usage) ```go proxy.WithClient(&fasthttp.Client{ TLSConfig: &tls.Config{InsecureSkipVerify: true}, }) // Forward to url app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif")) ``` `proxy.Balancer` also adopts the common middleware signature pattern and now accepts an optional variadic config: call `proxy.Balancer()` to use the defaults or continue passing a single `proxy.Config` value as in v2. #### Session `session.New()` now returns a middleware handler. When using the store pattern, create a store with `session.NewStore()` or call `Store()` on the middleware. Sessions obtained from a store must be released manually via `sess.Release()`. Additionally, replace the deprecated `KeyLookup` option with extractor functions such as `session.FromCookie()` or `session.FromHeader()`. Multiple extractors can be combined with `session.Chain()`. ```go // Before app.Use(session.New(session.Config{ KeyLookup: "cookie:session_id", Store: session.NewStore(), })) ``` ```go // After app.Use(session.New(session.Config{ Extractor: session.FromCookie("session_id"), Store: session.NewStore(), })) ``` See the [Session Middleware Migration Guide](./middleware/session.md#migration-guide) for complete details. --- ## 404 Handler # Custom 404 Not Found Handler Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/404-handler) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/404-handler) This example demonstrates how to implement a custom 404 Not Found handler using the [Fiber](https://gofiber.io) web framework in Go. The purpose of this example is to show how to handle requests to undefined routes gracefully by returning a 404 status code. ## Description In web applications, it's common to encounter requests to routes that do not exist. Handling these requests properly is important to provide a good user experience and to inform the user that the requested resource is not available. This example sets up a simple Fiber application with a custom 404 handler to manage such cases. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Running the Example To run the example, use the following command: ```bash go run main.go ``` The server will start and listen on `localhost:3000`. ## Example Routes - **GET /hello**: Returns a simple greeting message. - **Undefined Routes**: Any request to a route not defined will trigger the custom 404 handler. ## Custom 404 Handler The custom 404 handler is defined to catch all undefined routes and return a 404 status code with a "Not Found" message. ## Code Overview ### `main.go` ```go package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { // Fiber instance app := fiber.New() // Routes app.Get("/hello", hello) // 404 Handler app.Use(func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNotFound) // => 404 "Not Found" }) // Start server log.Fatal(app.Listen(":3000")) } // Handler func hello(c fiber.Ctx) error { return c.SendString("I made a ☕ for you!") } ``` ## Conclusion This example provides a basic setup for handling 404 Not Found errors in a Fiber application. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [GitHub Repository](https://github.com/gofiber/fiber) --- ## 👋 Overview # 🍳 Recipes for [Fiber](https://github.com/gofiber/fiber) **Welcome to the official Fiber cookbook**! Here you can find the most **delicious** recipes to cook delicious meals using our web framework. ## 🌽 Table of contents - [404 Handler](./404-handler/README.md) - Custom 404 error page handling. - [Air Live Reloading](./air/README.md) - Live reloading for Go applications. - [Asynq](./asynq/README.md) - Enqueue background jobs from Fiber and process them with an Asynq worker. - [Auth + Docker + Postgres + JWT](./auth-docker-postgres-jwt/README.md) - Authentication with Docker, Postgres, and JWT. - [Auth + JWT](./auth-jwt/README.md) - Simple JWT authentication. - [Autocert](./autocert/README.md) - Automatic TLS certificate management. - [AWS Elastic Beanstalk](./aws-eb/README.md) - Deploying to AWS Elastic Beanstalk. - [AWS SAM Container](./aws-sam-container/README.md) - Containerized serverless applications with AWS SAM. - [AWS SES Email Sender](./aws-ses-sender/README.md) - AWS SES-based Golang email delivery service. Provides email dispatch processing, status tracking, scheduled sending, and result analysis capabilities. - [Bootstrap](./bootstrap/README.md) - Integrating Bootstrap. - [Clean Architecture](./clean-architecture/README.md) - Implementing clean architecture in Go. - [Clean Code](./clean-code/README.md) - Implementing clean code in Go. - [Cloud Run](./cloud-run/README.md) - Deploying to Google Cloud Run. - [Cloudflare Container Workers with Go Fiber](./cloudflare-workers/README.md) - Run a Go Fiber v3 app in a Cloudflare Container Worker with a Worker proxy. - [Colly Gorm](./colly-gorm/README.md) - Web scraping with Colly and GORM. - [CSRF](./csrf/README.md) - Cross-Site Request Forgery (CSRF) protection. - [CSRF + Session](./csrf-with-session/README.md) - Cross-Site Request Forgery (CSRF) protection with session management. - [Docker + MariaDB](./docker-mariadb-clean-arch/README.md) - Dockerized MariaDB with Clean Architecture. - [Docker + Nginx](./docker-nginx-loadbalancer/README.md) - Load balancing with Docker and Nginx. - [Dummy JSON Proxy](./dummyjson/README.md) - Proxying dummy JSON data. - [Email Verification Service](./email-verification/README.md) - Email verification service with code generation and validation - [Entgo ORM (MySQL)](./ent-mysql/README.md) - Using Entgo ORM with MySQL - [Entgo Sveltekit](./entgo-sveltekit/README.md) - A full-stack Todo application built using Sveltekit, Tailwind CSS, Entgo, and SQLite. - [Envoy External Authorization](./envoy-extauthz/README.md) - External authorization with Envoy. - [File Server](./file-server/README.md) - Serving static files. - [Firebase Authentication](./firebase-auth/README.md) - Firebase authentication integration. - [Firebase Functions](./firebase-functions/README.md) - Using Firebase Functions. - [Firebase GCloud](./gcloud/README.md) - Integrating Firebase with Google Cloud. - [Google Cloud Firebase](./gcloud-firebase/README.md) - Firebase services on Google Cloud. - [GeoIP](./geoip/README.md) - Geolocation using ip-api.com. - [GeoIP + MaxMind](./geoip-maxmind/README.md) - Geolocation with GeoIP and MaxMind databases. - [GORM](./gorm/README.md) - Using GORM with SQLite database. - [GORM MySQL](./gorm-mysql/README.md) - Using GORM with MySQL database. - [GORM + PostgreSQL](./gorm-postgres/README.md) - Using GORM with PostgreSQL database. - [Graceful shutdown](./graceful-shutdown/README.md) - Graceful shutdown of applications. - [GraphQL](./graphql/README.md) - Setting up a GraphQL server. - [gRPC](./grpc/README.md) - Using Fiber as a client to a gRPC server. - [Hello World](./hello-world/README.md) - A simple "Hello, World!" application. - [Heroku](./heroku/README.md) - Deploying to Heroku. - [Hexagonal Architecture](./hexagonal/README.md) - A Hexagonal Software Architecture in Golang and MongoDB. - [HTTPS with PKCS12 TLS](./https-pkcs12-tls/README.md) - Setting up an HTTPS server with PKCS12 TLS certificates. - [HTTPS with TLS](./https-tls/README.md) - Setting up an HTTPS server with self-signed TLS certificates. - [I18n](./i18n/README.md) - Internationalization support. - [JWT](./jwt/README.md) - Using JSON Web Tokens (JWT) for authentication. - [Kubernetes](./k8s/README.md) - Deploying applications to Kubernetes. - [Todo App + Auth + GORM + Testcontainers](./local-development-testcontainers/README.md) - A Todo application with authentication using GORM and Postgres. - [Memgraph](./memgraph/README.md) - Using Memgraph. - [MinIO](./minio/README.md) - A simple application for uploading and downloading files from MinIO. - [MongoDB](./mongodb/README.md) - Connecting to a MongoDB database. - [Monitoring with Apitally](./monitoring-with-apitally/README.md) - A simple REST API with monitoring and request logging using Apitally. - [Multiple Ports](./multiple-ports/README.md) - Running an application on multiple ports. - [MySQL](./mysql/README.md) - Connecting to a MySQL database. - [Neo4j](./neo4j/README.md) - Connecting to a Neo4j database. - [OAuth2](./oauth2/README.md) - Implementing GitHub OAuth2 authentication with GoFiber. - [Google OAuth2](./oauth2-google/README.md) - Implementing Google OAuth2 authentication. - [OpenAPI](./openapi/README.md) - Generate OpenAPI 3 documentation and JSON schema for your application. - [Optional Parameter](./optional-parameter/README.md) - Handling optional parameters. - [Parsley](./parsley/README.md) - Using Parsley for dependency injection in an application. - [PostgreSQL](./postgresql/README.md) - Connecting to a PostgreSQL database. - [Prefork](./prefork/README.md) - Running an application in prefork mode. - [RabbitMQ](./rabbitmq/README.md) - Using RabbitMQ with Fiber to publish messages to a queue. - [React](./react-router/README.md) - Using React. - [Recover Middleware](./recover/README.md) - Recover middleware for error handling. - [RSS Feed](./rss-feed/README.md) - Generating an RSS feed. - [Seenode](./seenode/README.md) - Deploying to Seenode cloud platform. - [Server Timing](./server-timing/README.md) - Adding Server Timing headers to an application. - [Sessions + SQLite3](./sessions-sqlite3/README.md) - Using SQLite3 as a storage engine for user sessions. - [Socketio](./socketio/README.md) - A chatroom application using Socket.IO. - [Single Page Application (SPA)](./spa/README.md) - Setting up a Single Page Application (SPA) using React for the frontend and Go for the backend. - [Sqlboiler](./sqlboiler/README.md) - Using Sqlboiler ORM. - [Sqlc](./sqlc/README.md) - Using Sqlc to generate Go code from SQL queries. - [Server-Sent Events](./sse/README.md) - Implementing Server-Sent Events in an application. - [Stream Request Body](./stream-request-body/README.md) - Streaming request bodies. - [Svelte Netlify](./svelte-netlify/README.md) - Deploying a Svelte + Fiber application on Netlify. - [Sveltekit Embed](./sveltekit-embed/README.md) - A full-stack application built using Sveltekit and Tailwind CSS. - [Swagger](./swagger/README.md) - Generate Swagger documentation for your application. - [Tableflip Example](./tableflip/README.md) - Use tableflip for graceful upgrades in a Go application. - [Template](./template/README.md) - Setting up a Go application with template rendering. - [Template Asset Bundling](./template-asset-bundling/README.md) - Setting up a Go application with template rendering and asset bundling. - [Todo App + Auth + GORM](./todo-app-with-auth-gorm/README.md) - A Todo application with authentication using GORM. - [Unit Testing](./unit-test/README.md) - Writing unit tests for a Go Fiber application. - [File Upload](./upload-file/README.md) - Handling file uploads in a Go application. - [URL Shortener](./url-shortener-api/README.md) - URL shortening service with a simple API. - [Validation](./validation/README.md) - Input validation using go-playground/validator. - [Vercel](./vercel/README.md) - Deploy a Go application to Vercel. - [WebSocket](./websocket/README.md) - Real-time communication application using WebSockets. - [WebSocket Chat](./websocket-chat/README.md) - Real-time chat application using WebSockets. ## 👩‍🍳 Have a delicious recipe? If you have found an amazing recipe for **Fiber** — share it with others! We are ready to accept your [PR](https://github.com/gofiber/recipes/pulls) and add your recipe to the cookbook (both on [website](https://docs.gofiber.io) and this repository). --- ## Air Live Reloading # Live Reloading with Air Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/air) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/air) This example demonstrates how to set up live reloading for a Go application using the [Air](https://github.com/air-verse/air) tool. The purpose of this example is to show how to automatically reload your application during development whenever you make changes to the source code. ## Description Live reloading is a useful feature during development as it saves time by automatically restarting the application whenever changes are detected. This example sets up a simple Fiber application and configures Air to watch for changes and reload the application. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) - [Air](https://github.com/air-verse/air) ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/air ``` 2. Install the dependencies: ```bash go mod download ``` 3. Install Air: ```bash go install github.com/air-verse/air@latest ``` ## Configuration Air is configured using the `air/.air.conf` file. This file specifies the build command, binary name, and directories to watch for changes. The configuration files for different operating systems are provided: - `air/.air.windows.conf` for Windows - `air/.air.linux.conf` for Linux ## Running the Example To run the example with live reloading, use the following command: ```bash air -c .air.linux.conf ``` or for Windows: ```bash air -c .air.windows.conf ``` The server will start and listen on `localhost:3000`. Any changes to the source code will automatically trigger a rebuild and restart of the application. ## Example Routes - **GET /**: Returns a simple greeting message. ## Code Overview ### `main.go` ```go package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { // Create new Fiber instance app := fiber.New() // Create new GET route on path "/" app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) // Start server on http://localhost:3000 log.Fatal(app.Listen(":3000")) } ``` ## Conclusion This example provides a basic setup for live reloading a Go application using Air. It can be extended and customized further to fit the needs of more complex applications. ## References - [Air Documentation](https://github.com/air-verse/air) - [Fiber Documentation](https://docs.gofiber.io) - [GitHub Repository](https://github.com/gofiber/fiber) --- ## Asynq # Fiber and Asynq example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/asynq) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/asynq) ## Description This example shows how to run background jobs with [Asynq](https://github.com/hibiken/asynq) (a Redis-backed task queue) from a [Fiber](https://github.com/gofiber/fiber) HTTP server. The API enqueues a job and returns immediately; a separate worker process consumes the queue and does the slow work, with retries and priorities handled by Asynq. ## How it works - The Fiber API exposes `POST /enqueue` with a JSON body: `{"user_id": "...", "email": "..."}`. - Each request enqueues a `email:welcome` task onto Redis and returns the task id — it never does the work inline. - A separate **worker** process pulls tasks from Redis and runs them, retrying on failure with backoff. - The task type and payload live in a shared `task` package, so the API and the worker can't drift apart. The worker uses weighted queues (`critical` drained ~6x as often as `low`), which is the usual way to keep a noisy low-priority job from starving important ones. ## Requirements - [Go](https://golang.org/dl/) 1.25 or higher - A running Redis instance (or use the provided `docker-compose.yml`) ## Running the example ### With Docker Compose ```bash docker compose up --build ``` This starts Redis, the API, and the worker together. ### Manually Start Redis, then in two terminals: ```bash # terminal 1 — API make run-api # terminal 2 — worker make run-worker ``` Set `REDIS_ADDR` if Redis isn't on `localhost:6379`. ## Trying it out ```bash curl -X POST http://localhost:3000/enqueue \ -H "Content-Type: application/json" \ -d '{"user_id":"42","email":"jane@example.com"}' # {"enqueued":true,"task_id":"...","queue":"default"} ``` The worker terminal logs: ```text sending welcome email for user 42 ``` A request with a missing or invalid body returns `400`. ## Notes - The handler returns an error to signal a retry; returning `asynq.SkipRetry` (as it does for a malformed payload) tells Asynq not to bother retrying something that will never succeed. - Enqueue options like `asynq.MaxRetry` and `asynq.Queue` are set per call, so different endpoints can enqueue onto different queues with different retry policies. --- ## Auth + Docker + Postgres + JWT # Auth Docker Postgres JWT Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/auth-docker-postgres-jwt) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/auth-docker-postgres-jwt) This example demonstrates a boilerplate setup for a Go Fiber application that uses Docker, PostgreSQL, and JWT for authentication. ## Description This project provides a starting point for building a web application with user authentication using JWT. It leverages Docker for containerization and PostgreSQL as the database. ## Requirements - [Docker](https://www.docker.com/get-started) - [Docker Compose](https://docs.docker.com/compose/install/) - [Go](https://golang.org/dl/) 1.21 or higher ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/auth-docker-postgres-jwt ``` 2. Set the environment variables in a `.env` file (see `.env.example` for reference): ```env DB_PORT=5432 DB_USER=example_user DB_PASSWORD=example_password DB_NAME=example_db SECRET=example_secret ``` 3. Build and start the Docker containers: ```bash docker-compose build docker-compose up ``` The API and the database should now be running. ## Database Management You can manage the database via `psql` with the following command: ```bash docker-compose exec db psql -U ``` Replace `` with the value from your `.env` file. ## API Endpoints The following endpoints are available in the API: - **POST /api/user**: Register a new user. - **POST /api/auth/login**: Authenticate a user and return a JWT. - **GET /api/user/:id**: Get a user (requires a valid JWT). - **PATCH /api/user/:id**: Update a user (requires a valid JWT). - **DELETE /api/user/:id**: Delete a user (requires a valid JWT). ## Example Usage 1. Register a new user: ```bash curl -X POST http://localhost:3000/api/user -d '{"username":"testuser", "password":"testpassword", "email": "test@email.com"}' -H "Content-Type: application/json" ``` 2. Login to get a JWT: ```bash curl -X POST http://localhost:3000/api/auth/login -d '{"username":"testuser", "password":"testpassword"}' -H "Content-Type: application/json" ``` 3. Access a protected route: ```bash curl -H "Authorization: Bearer " http://localhost:3000/api/user/1 ``` Replace `` with the token received from the login endpoint. ## Conclusion This example provides a basic setup for a Go Fiber application with Docker, PostgreSQL, and JWT authentication. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [Docker Documentation](https://docs.docker.com) - [PostgreSQL Documentation](https://www.postgresql.org/docs/) - [JWT Documentation](https://jwt.io/introduction/) --- ## Auth + JWT # Auth JWT Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/auth-jwt) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/auth-jwt) This example demonstrates a boilerplate setup for a Go Fiber application that uses JWT for authentication. ## Description This project provides a starting point for building a web application with user authentication using JWT. It leverages Fiber for the web framework and GORM for ORM. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/auth-jwt ``` 2. Set the environment variables in a `.env` file (see `.env.sample`): ```env DB_HOST=localhost DB_PORT=5432 DB_USER=example_user DB_PASSWORD=example_password DB_NAME=example_db DB_SSLMODE=disable SECRET=example_secret ACCESS_TOKEN_TTL_MINUTES=15 ``` > **Note:** `DB_SSLMODE` controls the PostgreSQL SSL connection mode (`disable`, `require`, `verify-full`, etc.). Set to `require` or `verify-full` in production. 3. Install the dependencies: ```bash go mod download ``` 4. Run the application: ```bash go run main.go ``` The API should now be running on `http://localhost:3000`. ## Database Management You can manage the database via `psql` with the following command: ```bash psql -U -d -h localhost -p ``` Replace ``, ``, and `` with the values from your `.env` file. ## API Endpoints The following endpoints are available in the API: - **POST /api/auth/register**: Register a new user. - **POST /api/auth/login**: Authenticate a user and return a JWT. - **POST /api/auth/logout**: Logout a user and revoke their JWT. - **POST /api/auth/refresh-token**: Refreshes a user token and returns a JWT. - **GET /api/users/:id**: Get a user (requires a valid JWT). - **PATCH /api/users/:id**: Update a user (requires a valid JWT). - **DELETE /api/users/:id**: Delete a user (requires a valid JWT). - **GET /api/products**: Get all products. - **GET /api/products/:id**: Get a product. - **POST /api/products**: Create a new product (requires a valid JWT). - **DELETE /api/products/:id**: Delete a product (requires a valid JWT). ## Example Usage 1. Register a new user: ```bash curl -X POST http://localhost:3000/api/auth/register -d '{"username":"testuser", "password":"testpassword", "email":"test@example.com"}' -H "Content-Type: application/json" ``` 2. Login to get a JWT: ```bash curl -X POST http://localhost:3000/api/auth/login -d '{"email":"test@example.com", "password":"testpassword"}' -H "Content-Type: application/json" ``` 3. Access a protected route: ```bash curl -H "Authorization: Bearer " http://localhost:3000/api/users/1 ``` Replace `` with the token received from the login endpoint. ## Conclusion This example provides a basic setup for a Go Fiber application with JWT authentication. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [GORM Documentation](https://gorm.io/docs/) - [JWT Documentation](https://jwt.io/introduction/) --- ## Autocert # Autocert Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/autocert) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/autocert) This example demonstrates how to set up a secure Go Fiber application using Let's Encrypt for automatic TLS certificate management with `autocert`. ## Description This project provides a starting point for building a secure web application with automatic TLS certificate management using Let's Encrypt. It leverages Fiber for the web framework and `autocert` for certificate management. ## Requirements - [Go](https://golang.org/dl/) 1.21 or higher - [Git](https://git-scm.com/downloads) - A publicly accessible domain name pointing to your server - Ports 80 and 443 open and accessible from the internet (required by Let's Encrypt) ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/autocert ``` 2. Install the dependencies: ```bash go mod download ``` 3. Update the `HostPolicy` in `main.go` with your domain: ```go m := &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("yourdomain.com"), // Replace with your domain Cache: autocert.DirCache("./certs"), } ``` 4. Run the application: ```bash go run main.go ``` The application should now be running on `https://yourdomain.com`. ## Example Usage 1. Open your browser and navigate to `https://yourdomain.com` (replace with your actual domain). 2. You should see the message: `This is a secure server 👮`. ## Conclusion This example provides a basic setup for a Go Fiber application with automatic TLS certificate management using Let's Encrypt. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [Let's Encrypt Documentation](https://letsencrypt.org/docs/) - [Autocert Documentation](https://pkg.go.dev/golang.org/x/crypto/acme/autocert) --- ## AWS Elastic Beanstalk # AWS Elastic Beanstalk Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/aws-eb) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/aws-eb) This example demonstrates how to deploy a Go Fiber application to AWS Elastic Beanstalk. ## Description This project provides a starting point for deploying a Go Fiber application to AWS Elastic Beanstalk. It includes necessary configuration files and scripts to build and deploy the application. ## Requirements - [AWS CLI](https://aws.amazon.com/cli/) - [Elastic Beanstalk CLI](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install.html) - [Go](https://golang.org/dl/) 1.25 or higher - [Git](https://git-scm.com/downloads) ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/aws-eb ``` 2. Initialize Elastic Beanstalk: ```bash eb init ``` 3. Create an Elastic Beanstalk environment: ```bash eb create ``` 4. Deploy the application: ```bash eb deploy ``` ## Build Process The build process is defined in the `Buildfile` and `build.sh` scripts. - `Buildfile`: ```ruby make: ./build.sh ``` - `build.sh`: ```bash #!/bin/bash -xe # Get dependencies go mod download # Build the binary go build -o application application.go # Modify permissions to make the binary executable. chmod +x application ``` ## Application Code The main application code is in `application.go`: ```go package main import ( "log" "os" "github.com/gofiber/fiber/v3" ) func main() { // Initialize the application app := fiber.New() // Hello, World! app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) // Listen and Serve on 0.0.0.0:$PORT port := os.Getenv("PORT") if port == "" { port = "5000" } log.Fatal(app.Listen(":" + port)) } ``` ## .gitignore The `.gitignore` file includes configurations to ignore Elastic Beanstalk specific files: ```plaintext # Elastic Beanstalk Files .elasticbeanstalk/* !.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.global.yml ``` ## Conclusion This example provides a basic setup for deploying a Go Fiber application to AWS Elastic Beanstalk. It can be extended and customized further to fit the needs of more complex applications. ## References - [AWS Elastic Beanstalk Documentation](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/Welcome.html) - [Fiber Documentation](https://docs.gofiber.io) --- ## AWS SAM Container [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/aws-sam-container) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/aws-sam-container) This is a sample template for app - Below is a brief explanation of what we have generated for you: ```bash . ├── README.md <-- This instructions file ├── app <-- Source code for a lambda function │ ├── main.go <-- Lambda function code │ └── Dockerfile <-- Dockerfile ├── samconfig.toml <-- SAM CLI configuration file └── template.yaml ``` ## Features - [x] Use distroless image to build, The image size is only a few MB. - [x] Migrate to AWS SAM without changing your faber code using [aws-lambda-adapter](https://github.com/awslabs/aws-lambda-web-adapter). ## Requirements * AWS CLI already configured with Administrator permission * [Docker installed](https://www.docker.com/community-edition) * SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) You may need the following for local testing. * [Golang](https://golang.org) ## Setup process ### Installing dependencies & building the target In this example we use the built-in `sam build` to build a docker image from a Dockerfile and then copy the source of your application inside the Docker image. Read more about [SAM Build here](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) ### Local development **Invoking function locally through local API Gateway** ```bash docker run -it -p 80:3000 lambdafunction curl http://localhost Hello, World! ``` ## Packaging and deployment ```bash sam deploy --guided ``` The command will package and deploy your application to AWS, with a series of prompts: * **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. * **AWS Region**: The AWS region you want to deploy your app to. * **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. * **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. * **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. You can find your API Gateway Endpoint URL in the output values displayed after deployment. ## Add Permission to the Lambda Function for Public Access After deploying your Lambda function with an associated function URL, you might encounter a scenario where the function URL is not accessible due to missing permissions for public access. This is common when the authentication type for the function URL is set to "None," indicating that the function is intended to be publicly accessible without authentication. To ensure your Lambda function URL can be invoked publicly, you need to add the necessary permission that allows unauthenticated requests. This step is crucial when your function URL's authentication type is "None" but lacks the requisite permissions for public invocation. Manually Configuring Permissions You can manually configure permissions through the AWS Lambda console by creating a resource-based policy that grants the lambda:invokeFunctionUrl permission to all principals (*). This approach is straightforward but not suitable for automation within deployment pipelines. Automating Permission Configuration For a more automated approach, especially useful in CI/CD pipelines, you can use the AWS CLI or SDKs to add the necessary permissions after deploying your Lambda function. This can be incorporated into your deployment scripts or CI/CD workflows. Here is an example AWS CLI command that adds the required permission for public access to your Lambda function URL: ```shell aws lambda add-permission \ --function-name \ --action lambda:InvokeFunctionUrl \ --principal "*" \ --function-url-auth-type "NONE" \ --statement-id unique-statement-id ``` This command grants permission to all principals (*) to invoke your Lambda function URL, enabling public access as intended. # Appendix ### Golang installation Please ensure Go 1.25 (or newer in the Go 1.x series) is installed as per the instructions on the official golang website: https://golang.org/doc/install A quickstart way would be to use Homebrew, chocolatey or your linux package manager. #### Homebrew (Mac) Issue the following command from the terminal: ```shell brew install golang ``` If it's already installed, run the following command to ensure it's the latest version: ```shell brew update brew upgrade golang ``` #### Chocolatey (Windows) Issue the following command from the powershell: ```shell choco install golang ``` If it's already installed, run the following command to ensure it's the latest version: ```shell choco upgrade golang ``` --- ## AWS SES Email Sender [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/aws-ses-sender) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/aws-ses-sender) This is an AWS SES-based Golang email delivery service that extracts only the basic sending functionality from [my open-source project](https://github.com/lee-lou2/aws-ses-sender-go). ## Features - [x] Email sending using AWS SES - [x] Designed with daily limits and per-second sending rates in mind - [x] Scheduled sending and message grouping - [x] Email open tracking and result collection - [x] View daily sending counts and delivery results by message group ## Flowchart ```mermaid flowchart TD A[Client] -->|Email Send Request| B[API Server] B -->|DB Storage| C[Scheduler] C -->|Query Pending| D[Sender] D -->|SES Send| E[AWS SES] E -->|Email Received| F[Recipient] E -->|SNS Callback| G[API Server] G -->|DB Storage| H[Results/Statistics] F -->|Open Tracking| I[API Server] I -->|DB Storage| J[Open Events] H -->|Stats/Results Query| K[Client] J -->|Stats/Results Query| K[Client] ``` ## Requirements ### Essential Requirements - Go 1.25 or higher - AWS account and configuration - AWS SES service activated - Sender email or domain verification completed - IAM user with SES permissions - AWS Access Key and Secret Key - PostgreSQL 14.0 or higher - (Optional) Docker ### AWS SES Configuration 1. Verify sender email/domain in AWS SES console 2. Request removal from SES sandbox mode (for production) 3. Create SNS topic and set up SES feedback notifications 4. Grant following permissions to IAM user - `ses:SendEmail` - `ses:SendRawEmail` - `sns:Publish` (if using SNS for delivery notifications) - `sns:Subscribe` (if using SNS for delivery notifications) ## Project Structure ```plaintext aws-ses-sender/ ├── main.go # Application entry point ├── api/ # HTTP API related code │ ├── handler.go # API handler functions │ ├── route.go # API routing configuration │ ├── server.go # HTTP server setup/execution │ └── middlewares.go # API authentication middleware ├── cmd/ # Background job code │ ├── scheduler.go # Pending email scheduler │ └── sender.go # SES email sending processor ├── config/ # Application settings │ ├── env.go # Environment variable management │ └── db.go # Database connection settings ├── model/ # Database models │ └── email.go # GORM model definitions └── pkg/aws/ # AWS service integration └── ses.go # SES email sending ``` ## Setup ### Prerequisites - Go language development environment - AWS account and SES service configuration - Sender email/domain verification - IAM user creation with SES permissions - PostgreSQL database - (Optional) Sentry DSN ### Configuration Create a `.env` file in the project root and set the following environment variables: ```env # AWS Related AWS_ACCESS_KEY_ID=your_access_key AWS_SECRET_ACCESS_KEY=your_secret_key AWS_REGION=ap-northeast-2 EMAIL_SENDER=sender@example.com # Server and API SERVER_PORT=3000 API_KEY=your_api_key SERVER_HOST=http://localhost:3000 # Database (PostgreSQL) DB_HOST=localhost DB_PORT=5432 DB_USER=postgres DB_PASSWORD=postgres DB_NAME=postgres # Sending rate per second EMAIL_RATE=14 # Sentry (Optional) SENTRY_DSN=your_sentry_dsn ``` ### Installation and Execution 1. Clone repository: ```bash git clone cd aws-ses-sender ``` 2. Install dependencies: ```bash go mod tidy ``` 3. Run application: ```bash go run main.go ``` ## API Endpoints ### Email Sending Request ```http POST /v1/messages ``` Request body example: ```json { "messages": [ { "topicId": "promotion-event-2024", "emails": ["recipient1@example.com", "recipient2@example.com"], "subject": "Special Promotion Notice", "content": "

Hello!

Check out our special promotion details.

", "scheduledAt": "2024-12-25T10:00:00+09:00" } ] } ``` ### View Topic Sending Statistics ```http GET /v1/topics/:topicId ``` ### Email Open Tracking ```http GET /v1/events/open?requestId={requestId} ``` ### View Sending Statistics ```http GET /v1/events/counts/sent?hours={hours} ``` ### Receive Sending Results (AWS SNS) ```http POST /v1/events/results ``` --- ## Bootstrap [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/bootstrap) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/bootstrap) Fiber bootstrap for rapid development using Go-Fiber / Gorm / Validator. ## Components * Fiber * Html Engine Template * Logger * Monitoring * Gorm * PGSQL Driver * Validator * Env File ## Router API Router `/api` with rate limiter middleware Http Router `/` with CORS and CSRF middleware ## Setup 1. Copy the example env file over: ``` cp .env.example .env ``` 2. Modify the env file you just copied `.env` with the correct credentials for your database. Make sure the database you entered in `DB_NAME` has been created. 3. Run the API: ``` go run main.go ``` Your api should be running at `http://localhost:4000/` if the port is in use you may modify it in the `.env` you just created. --- ## Clean Architecture # Clean Architecture Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/clean-architecture) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/clean-architecture) This example demonstrates a Go Fiber application following the principles of Clean Architecture. ## Description This project provides a starting point for building a web application with a clean architecture. It leverages Fiber for the web framework, MongoDB for the database, and follows the Clean Architecture principles to separate concerns and improve maintainability. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [MongoDB](https://www.mongodb.com/try/download/community) - [Git](https://git-scm.com/downloads) ## Project Structure - `api/`: Contains the HTTP handlers, routes, and presenters. - `pkg/`: Contains the core business logic and entities. - `cmd/`: Contains the main application entry point. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/clean-architecture ``` 2. Set the environment variables in a `.env` file (see `.env.example`): ```env MONGO_URI=mongodb://localhost:27017 ``` 3. Install the dependencies: ```bash go mod download ``` 4. Run the application: ```bash go run main.go ``` The API should now be running on `http://localhost:3000`. ## API Endpoints The following endpoints are available in the API: - **GET /books**: List all books. - **POST /books**: Add a new book. - **PUT /books**: Update an existing book. - **DELETE /books**: Remove a book. ## Example Usage 1. Add a new book: ```bash curl -X POST http://localhost:3000/books -d '{"title":"Book Title", "author":"Author Name"}' -H "Content-Type: application/json" ``` 2. List all books: ```bash curl http://localhost:3000/books ``` 3. Update a book: ```bash curl -X PUT http://localhost:3000/books -d '{"id":"", "title":"Updated Title", "author":"Updated Author"}' -H "Content-Type: application/json" ``` 4. Remove a book: ```bash curl -X DELETE http://localhost:3000/books -d '{"id":""}' -H "Content-Type: application/json" ``` Replace `` with the actual ID of the book. ## Clean Architecture Principles Clean Architecture is a software design philosophy that emphasizes the separation of concerns, making the codebase more maintainable, testable, and scalable. In this example, the Go Fiber application follows Clean Architecture principles by organizing the code into distinct layers, each with its own responsibility. ### Layers in Clean Architecture 1. **Entities (Core Business Logic)** - Located in the `pkg/entities` directory. - Contains the core business logic and domain models, which are independent of any external frameworks or technologies. 2. **Use Cases (Application Logic)** - Located in the `pkg/book` directory. - Contains the application-specific business rules and use cases. This layer orchestrates the flow of data to and from the entities. 3. **Interface Adapters (Adapters and Presenters)** - Located in the `api` directory. - Contains the HTTP handlers, routes, and presenters. This layer is responsible for converting data from the use cases into a format suitable for the web framework (Fiber in this case). 4. **Frameworks and Drivers (External Interfaces)** - Located in the `cmd` directory. - Contains the main application entry point and any external dependencies like the web server setup. ### Example Breakdown - **Entities**: The `entities.Book` struct represents the core business model for a book. - **Use Cases**: The `book.Service` interface defines the methods for interacting with books, such as `InsertBook`, `UpdateBook`, `RemoveBook`, and `FetchBooks`. - **Interface Adapters**: The `handlers` package contains the HTTP handlers that interact with the `book.Service` to process HTTP requests and responses. - **Frameworks and Drivers**: The `cmd/main.go` file initializes the Fiber application and sets up the routes using the `routes.BookRouter` function. ### Code Example #### `entities/book.go` ```go package entities import "go.mongodb.org/mongo-driver/bson/primitive" type Book struct { ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` Title string `json:"title"` Author string `json:"author"` } ``` #### `pkg/book/service.go` ```go package book import "clean-architecture/pkg/entities" type Service interface { InsertBook(book *entities.Book) (*entities.Book, error) UpdateBook(book *entities.Book) (*entities.Book, error) RemoveBook(ID string) error FetchBooks() (*[]entities.Book, error) } ``` #### `api/handlers/book_handler.go` ```go package handlers import ( "clean-architecture/pkg/book" "clean-architecture/pkg/entities" "clean-architecture/api/presenter" "github.com/gofiber/fiber/v3" "net/http" "errors" ) func AddBook(service book.Service) fiber.Handler { return func(c fiber.Ctx) error { var requestBody entities.Book err := c.Bind().Body(&requestBody) if err != nil { c.Status(http.StatusBadRequest) return c.JSON(presenter.BookErrorResponse(err)) } if requestBody.Author == "" || requestBody.Title == "" { c.Status(http.StatusBadRequest) return c.JSON(presenter.BookErrorResponse(errors.New("Please specify title and author"))) } result, err := service.InsertBook(&requestBody) if err != nil { c.Status(http.StatusInternalServerError) return c.JSON(presenter.BookErrorResponse(err)) } return c.JSON(presenter.BookSuccessResponse(result)) } } ``` #### `main.go` ```go package main import ( "clean-architecture/api/routes" "clean-architecture/pkg/book" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() bookRepo := book.NewRepo(collection) bookService := book.NewService(bookRepo) routes.BookRouter(app, bookService) app.Listen(":3000") } ``` By following Clean Architecture principles, this example ensures that each layer is independent and can be modified or replaced without affecting the other layers, leading to a more maintainable and scalable application. ## Conclusion This example provides a basic setup for a Go Fiber application following Clean Architecture principles. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [MongoDB Documentation](https://docs.mongodb.com/) - [Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) --- ## Clean Code # Clean Code Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/clean-code) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/clean-code) This is an example of a RESTful API built using the Fiber framework (https://gofiber.io/) and PostgreSQL as the database. ## Description of Clean Code Clean code is a philosophy and set of practices aimed at writing code that is easy to understand, maintain, and extend. Key principles of clean code include: - **Readability**: Code should be easy to read and understand. - **Simplicity**: Avoid unnecessary complexity. - **Consistency**: Follow consistent coding standards and conventions. - **Modularity**: Break down code into small, reusable, and independent modules. - **Testability**: Write code that is easy to test. This Fiber app is a good example of clean code because: - **Modular Structure**: The code is organized into distinct modules, making it easy to navigate and understand. - **Clear Separation of Concerns**: Different parts of the application (e.g., routes, handlers, services) are clearly separated, making the codebase easier to maintain and extend. - **Error Handling**: Proper error handling is implemented to ensure the application behaves predictably. ## Start 1. Build and start the containers: ```sh docker compose up --build ``` 1. The application should now be running and accessible at `http://localhost:3000`. ## Endpoints - `GET /api/v1/books`: Retrieves a list of all books. ```sh curl -X GET http://localhost:3000/api/v1/books ``` - `POST /api/v1/books`: Adds a new book to the collection. ```sh curl -X POST http://localhost:3000/api/v1/books \ -H "Content-Type: application/json" \ -d '{"title":"Title"}' ``` --- ## Cloud Run # Cloud Run Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/cloud-run) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/cloud-run) This example demonstrates how to deploy a Go Fiber application to Google Cloud Run. ## Description This project provides a starting point for deploying a Go Fiber application to Google Cloud Run. It includes necessary configuration files and scripts to build and deploy the application using Docker and Google Cloud Build. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Docker](https://www.docker.com/get-started) - [Google Cloud SDK](https://cloud.google.com/sdk/docs/install) - [Git](https://git-scm.com/downloads) ## GCP Prerequisites Before deploying, ensure the following GCP APIs are enabled and IAM roles are granted: **Enable APIs:** ```bash gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ containerregistry.googleapis.com ``` **IAM roles required for the Cloud Build service account (`[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com`):** - `roles/run.admin` — deploy Cloud Run services - `roles/iam.serviceAccountUser` — act as the Cloud Run runtime service account - `roles/storage.admin` — push images to Container Registry ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/cloud-run ``` 2. Install the dependencies: ```bash go mod download ``` 3. Build the Docker image: ```bash docker build -t cloud-run-example . ``` 4. Run the Docker container: ```bash docker run -p 3000:3000 cloud-run-example ``` The application should now be running on `http://localhost:3000`. > **PORT environment variable:** Cloud Run injects the `PORT` env variable at runtime. The application reads `PORT` and falls back to `3000` if unset, so it works both locally and on Cloud Run without any code changes. ## Deploy to Google Cloud Run 1. Set up Google Cloud SDK and authenticate: ```bash gcloud auth login gcloud config set project [YOUR_PROJECT_ID] ``` 2. Build and push the Docker image using Google Cloud Build: ```bash gcloud builds submit --tag gcr.io/[YOUR_PROJECT_ID]/cloud-run-example ``` 3. Deploy the image to Cloud Run: ```bash gcloud run deploy cloud-run-example --image gcr.io/[YOUR_PROJECT_ID]/cloud-run-example --platform managed --region [YOUR_REGION] --allow-unauthenticated ``` > **Note:** `--allow-unauthenticated` makes the service publicly accessible. Remove this flag in production and use IAM-based access control instead. Replace `[YOUR_PROJECT_ID]` and `[YOUR_REGION]` with your Google Cloud project ID and desired region. ## Cloud Build Configuration The `cloudbuild.yaml` file defines the steps to build and deploy the application using Google Cloud Build: ```yaml steps: - name: 'gcr.io/kaniko-project/executor:latest' id: 'build-and-push' args: - '--destination=asia.gcr.io/$PROJECT_ID/$_SERVICE_NAME:$SHORT_SHA' - '--destination=asia.gcr.io/$PROJECT_ID/$_SERVICE_NAME:latest' - '--dockerfile=Dockerfile' - '--context=.' - '--cache=true' - '--cache-ttl=120h' - id: 'Deploy to Cloud Run' name: 'gcr.io/cloud-builders/gcloud' entrypoint: 'bash' args: - '-c' - | gcloud run deploy $_SERVICE_NAME \ --image=asia.gcr.io/$PROJECT_ID/$_SERVICE_NAME:$SHORT_SHA \ --region=$_REGION --platform managed \ --allow-unauthenticated \ --port=3000 # NOTE: --allow-unauthenticated is for demo purposes only. # Remove this flag in production and use IAM-based access control instead. options: substitutionOption: ALLOW_LOOSE substitutions: _SERVICE_NAME: cloud-run-example _REGION: asia-southeast1 ``` ## Example Usage 1. Open your browser and navigate to the Cloud Run service URL provided after deployment. 2. You should see the message: `Hello, World!`. ## Conclusion This example provides a basic setup for deploying a Go Fiber application to Google Cloud Run. It can be extended and customized further to fit the needs of more complex applications. ## References - [Google Cloud Run Documentation](https://cloud.google.com/run/docs) - [Fiber Documentation](https://docs.gofiber.io) - [Docker Documentation](https://docs.docker.com/) --- ## Cloudflare Container Workers with Go Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/cloudflare-workers) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/cloudflare-workers) This example demonstrates how to use [Go Fiber v3](https://github.com/gofiber/fiber) with [Cloudflare Container Workers](https://developers.cloudflare.com/containers/). ## Features - **Go Fiber v3** framework - **Distroless container** for minimal attack surface (`gcr.io/distroless/static-debian12`) - **JSON API** response - **Environment variables** support - **Logger and Recover** middleware ## Prerequisites - Bun - Go 1.25+ - Wrangler CLI - Cloudflare account with Container Workers access ## Getting Started 1. Install dependencies: ```bash bun install ``` 2. Run locally: ```bash bun run dev ``` 3. Deploy to Cloudflare: ```bash bun run deploy ``` ## Project Structure ```text . ├── src/index.ts # Worker entry point ├── container_src/ │ ├── main.go # Go Fiber application │ ├── go.mod # Go module file │ └── go.sum # Go dependencies ├── Dockerfile # Container configuration └── wrangler.jsonc # Cloudflare Workers configuration ``` ## How it Works 1. The Worker (TypeScript) receives HTTP requests. 2. Requests are forwarded to the Go Fiber container. 3. The container responds with JSON data, including environment variables. ## Container Configuration The container is configured with: - 2-minute sleep timeout for inactivity - Environment variable `MESSAGE` passed from the container class - Port 8080 (default) ## Learn More - [Fiber Documentation](https://docs.gofiber.io/) - [Cloudflare Container Workers](https://developers.cloudflare.com/containers/) - [Cloudflare Workers](https://developers.cloudflare.com/workers/) --- ## Colly Gorm # Simple Web Scraping Colly App with Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/colly-gorm) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/colly-gorm) A Go application using [Fiber](https://gofiber.io), [Colly v2](https://go-colly.org/), and [GORM](https://gorm.io) to scrape websites and persist data in PostgreSQL. ## Prerequisites - [Docker](https://docs.docker.com/get-docker/) and Docker Compose ## How to Run 1. Clone the repository. 2. Navigate to the project directory: `cd colly-gorm` 3. Copy the example env file: `cp app/app.env.example app/app.env` 4. Start the stack: `docker compose up --build` ## Project Structure ``` colly-gorm/ ├── app/ │ ├── app.env.example # Environment variable template │ ├── Dockerfile │ ├── go.mod │ ├── cmd/ │ │ └── api/ │ │ └── main.go # App entry point, Fiber routes │ └── internals/ │ ├── consts/ │ │ └── consts.go # Config loading via Viper │ └── services/ │ ├── database/ │ │ ├── database.go # GORM connection │ │ └── models.go # Quote and Course models │ └── scrapers/ │ ├── toscrape.go # Quotes scraper │ └── coursera_courses.go # Coursera scraper ├── db/ │ └── create_db.sql # DB initialization └── docker-compose.yml ``` ## API Endpoints | Method | Path | Description | |--------|------|-------------| | `GET` | `/api/healthchecker` | Health check — returns service status | | `GET` | `/scrape/quotes` | Triggers async scraping of [quotes.toscrape.com](http://quotes.toscrape.com) and stores results in PostgreSQL | | `GET` | `/scrape/coursera` | Triggers async scraping of [coursera.org/browse](https://www.coursera.org/browse) and stores course data in PostgreSQL | Scraping jobs run asynchronously; the endpoint returns immediately while scraping continues in the background. ## Database Models **Quote** - `author` — quote author - `quote` — quote text **Course** - `title`, `description`, `creator`, `url`, `rating` ## Environment Variables See `app/app.env.example`: ```env POSTGRES_HOST=colly_db POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=colly ``` ## What It Does - Registers Colly HTML callbacks before visiting pages (correct callback order). - Scrapes data from websites and stores it in a PostgreSQL database via GORM. - Uses Fiber middleware (logger, CORS) applied globally before sub-app routing. --- ## CSRF(Csrf) # CSRF Examples [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/csrf) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/csrf) Example Cross Site Request Forgery (CSRF) vulnerabilities in action. ## Requirements * [git](https://git-scm.com/downloads) * [Golang](https://golang.org/) ## Install Go Modules Like any golang project, you will need to download and install the required modules for the project to run. Change into the "csrf" directory: ```bash cd csrf ``` And then: ```bash go mod vendor && go mod download && go mod tidy ``` This command installs the golang dependencies needed to run the project in a new directory named `vendor`. Once the modules have finished installing, you can run the project like this: ```bash go run main.go ``` OR ```bash go run main.go withoutCsrf ``` You should see the following if everything is OK: ``` Server started and listening at localhost:3000 ``` ## Try the demo Start the server without csrf, to see the dangers of these attacks ```bash go run main.go withoutCsrf ``` Open your browser to and navigate to [localhost:3000](http://localhost:3000). Login using the test account: * Username: `bob` * Password: `test` In a new tab, navigate to [localhost:3001](http://localhost:3001) to view some examples of CSRF exploits. You will notice that the balance goes down everytime you load that page. This is because the page is successfully exploiting a CSRF vulnerability. ## See the "fixed" version To see the csrf version of this demo, just stop the server by pressing __CTRL + C__ to kill the server process and then run ```bash go run main.go ``` Navigate again to [localhost:3000](http://localhost:3000) and login to the test account. And once more try the page with the CSRF exploits: [localhost:3001](http://localhost:3001). You will notice now that the account balance is unchanged. ## Going further Here are some useful links where you can learn more about this topic: * https://en.wikipedia.org/wiki/Cross-site_request_forgery * https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) --- ## CSRF + Session # CSRF-with-session Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/csrf-with-session) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/csrf-with-session) Example GoFiber web app using Cross Site Request Forgery (CSRF) middleware with session. This example impliments multiple best-practices for CSRF protection: - CSRF Tokens are linked to the user's session. - Pre-sessions are used, so that CSRF tokens are always available, even for anonymous users (eg for login forms). - Cookies are set with a defense-in-depth approach: - Secure: true - HttpOnly: true - SameSite: Lax - Expiration: 30 minutes (of inactivity) - Cookie names are prefixed with "__Host-" (see [MDN-Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for more information)) ## Requirements * [git](https://git-scm.com/downloads) * [Golang](https://golang.org/) ## Install Go Modules Like any golang project, you will need to download and install the required modules for the project to run. Change into the "csrf-with-session" directory: ```bash cd csrf-with-session ``` And then: ```bash go mod vendor && go mod download && go mod tidy ``` This command installs the golang dependencies needed to run the project in a new directory named `vendor`. Once the modules have finished installing, you can run the project like this: ```bash go run main.go ``` You should see the following if everything is OK: ``` Server started and listening at 127.0.0.1:8443 ``` ## Try the demo Start the server by running: ```bash go run main.go ``` Open your browser to and navigate to [127.0.0.1:8443](http://127.0.0.1:8443). ### Accept the self-signed certificate warning and visit the site. In Chrome: - Click on "Advanced" - Click on "Proceed to 127.0.0.1:8443 (unsafe)" In Firefox: - Click on "Advanced" - Click on "Accept the Risk and Continue" In Safari: - Click on "Show Details" - Click on "visit this website" ### Try to access the /protected page Login using one of the test accounts: * Username: `user1` * Password: `password1` OR * Username: `user2` * Password: `password2` Once logged in, you will be able to see the /protected page. ### Submit the form on the /protected page Once logged in, you will be able to see the /protected page. The /protected page contains a form that submits to the /protected page. If you try to submit the form without a valid CSRF token, you will get a 403 Forbidden error. ## CSRF Protection All methods except GET, HEAD, OPTIONS, and TRACE are checked for the CSRF token. If the token is not present or does not match the token in the session, the request is aborted with a 403 Forbidden error. ## Token Lifecycle The CSRF token is generated when the user visits any page on the site. The token is stored in the session and is valid for until it expires, or the authorization scope changes (e.g. the user logs in, or logs out). It is important that CSRF tokens do not persist beyond the scope of the user's session, that a new session is created when the user logs in, and that the session is destroyed when the user logs out. The CSRF middleware has a `SingleUseToken` configuration option that can be used to generate a new token for each request. This is useful for some applications, but is not used in this example. Single use tokens have usability implications in scenarios where the user has multiple tabs open, or when the user uses the back button in their browser. ## Session Storage Sessions are stored in memory for this example, but you can use any session store you like. See the [Fiber session documentation](https://docs.gofiber.io/api/middleware/session) for more information. ### Note on pre-sessions GoFiber's CSRF middleware will automatically create a session if one does not exist. That means that we always have pre-sessions when using the CSRF middleware. In this example we set a session variable `loggedIn` to `true` when the user logs in, in order to distinguish between logged in and logged out users. ## Going further Here are some useful links where you can learn more about this topic: * https://en.wikipedia.org/wiki/Cross-site_request_forgery * https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) --- ## Docker + MariaDB # Docker MariaDB Clean Architecture [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/docker-mariadb-clean-arch) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/docker-mariadb-clean-arch) A slightly complex REST application with Fiber to showcase Clean Architecture with MariaDB as a dependency with Docker. ## Prerequisites - Docker Compose for running the application. - Shell that supports `sh`, `make`, and `curl` for end-to-end testing. UNIX systems or WSL should work fine. - Postman if you want to test this API with GUI. ## Application This application is a slightly complex example of a REST API that have four major endpoints. A public user can access the `User`, `Auth`, and `Misc` major endpoints, but they cannot access the `City` endpoint (as it is protected). If one wants to access said endpoint, they have to log in first via the `Auth` endpoint, and only after that they can access the `City` endpoint. This application uses MariaDB as a database (dockerized), and JWT as an authentication mechanism. This application also showcases how to perform 1-to-many relational mapping in Clean Architecture (one user can have multiple cities), and also the implementation of `JOIN` SQL clause in Go in general. ## Clean Architecture ![Clean Architecture](./assets/CleanArchitecture.jpg) Clean Architecture is a concept introduced by Robert C. Martin or also known as Uncle Bob. Simply put, the purpose of this architecture is to perform complete separation of concerns. Systems made this way can be independent of frameworks, testable (easy to write unit tests), independent of UI, independent of database, and independent of any external agency. When you use this architecture, it is simple to change the UI, the database, or the business logic. One thing that you should keep in mind when using this architecture is about Dependency Rule. In Clean Architecture, source code dependency can only point inwards. This means that the 'inner circle' of the system cannot know at all about the outside world. For example, in the diagram above, use-cases knows about entities, but entities cannot know about use-cases. Data formats used in outer circle should not be used by an inner circle. Because of this, when you change something that is located the innermost of the circle (entities for example), usually you have to change the outer circles. However, if you change something that is not the innermost of the circle (controllers for example), you do not need to change the use-cases and the entities (you may have to change the frameworks and drivers as they are dependent on each other). If you want to learn more about Clean Architecture, please see the articles that I have attached below as references. ## System Architecture For the sake of clearness, here is the diagram that showcases the system architecture of this API. ![System Architecture](./assets/SystemArchitecture.png) Please refer to below table for terminologies / filenames for each layers that are used in this application. The project structure is referred from [this project](https://github.com/golang-standards/project-layout). In the `internal` package, there are packages that are grouped according to their functional responsibilities. If you open the package, you will see the files that represents the Clean Architecture layers. For the dependency graph, it is straightforward: handler/middleware depends on service, service depends on repository, and repository depends on domain and the database (via dependency injection). All of the layers are implemented with the said infrastructure (Fiber, MariaDB, and Authentication Service) in above image. I have slightly modified the layers in this application to conform to my own taste of Clean Architecture. | Architecture Layer | Equivalent Layer | Filename | | :-----------------: | :--------------------: | :------------------------------: | | External Interfaces | Presenters and Drivers | `middleware.go` and `handler.go` | | Controllers | Business Logic | `service.go` | | Use Cases | Repositories | `repository.go` | | Entities | Entities | `domain.go` | Basically, a request will have to go through `handler.go` (and `middleware.go`) first. After that, the program will call a repository or a use-case that is requested with `service.go`. That controller (`service.go`) will call `repository.go` that conforms to the `domain.go` in order to fulfill the request that the `service.go` asked for. The result of the request will be returned back to the user by `handler.go`. In short: - `handler.go` and `middleware.go` is used to receive and send requests. - `service.go` is business-logic or controller (some might have different opinions, but this is my subjective opinion). - `repository.go` is used to interact to the database (use-case). - `domain.go` is the 'shape' of the data models that the program use. For the sake of completeness, here are the functional responsibilities of the project structure. - `internal/auth` is used to manage authentication. - `internal/city` is used to manage cities. This endpoint **is protected**. - `internal/infrastructure` is used to manage infrastructure of the application, such as MariaDB and Fiber. - `internal/misc` is used to manage miscellaneous endpoints. - `internal/user` is used to manage users. This endpoint is **not protected**. Please refer to the code itself for further details. I commented everything in the code, so I hope it is clear enough! ## API Endpoints / Features This API is divided into four 'major endpoints', which are miscellaneous, users, authentication, and cities. ### Miscellaneous Endpoints classified here are miscellaneous endpoints. - `GET /api/v1` for health check. ### Users Endpoints classified here are endpoints to perform operation on 'User' domain. - `GET /api/v1/users` to get all users. - `POST /api/v1/users` to create a user. - `GET /api/v1/users/` to get a user. - `PUT /api/v1/users/` to update a user. - `DELETE /api/v1/users/` to delete a user. ### Authentication Endpoints classified here are endpoints to perform authentication. In my opinion, this is framework-layer / implementation detail, so there is no 'domain' regarding this endpoint and you can use this endpoint as an enhancement to other endpoints. Authentication in this API is done using JSON Web Tokens. - `POST /api/v1/auth/login` to log in as the user with ID of 1 in the database. Will return JWT and said JWT will be stored in a cookie. - `POST /api/v1/auth/logout` to log out. This route removes the JWT from the cookie. - `GET /api/v1/auth/private` to access a private route which displays information about the current (valid) JWT. ### Cities Endpoints classified here are endpoints to perform operation on `City` domain. **Endpoints here are protected via JWT in the cookie**, so if you are going to use this endpoint, make sure you are logged in first (or at least have a valid JWT). - `GET /api/v1/cities` to get all cities. - `POST /api/v1/cities` to create a new city. - `GET /api/v1/cities/` to get a city. - `PUT /api/v1/cities/` to update a city. - `DELETE /api/v1/cities/` to delete a city. ## Installation In order to run this application, you just need to do the following commands. - Clone the repository. ```bash git clone git@github.com:gofiber/recipes.git ``` - Switch to this repository. ```bash cd recipes/docker-mariadb-clean-arch ``` - Run immediately with Docker. After you run this command, migration script will be automatically run to populate your dockerized MariaDB. ```bash make start ``` - Test with Postman (set the request URL to `localhost:8080`) or with the created end-to-end testing script. Keep in mind that the end-to-end script is only available for the first run. If you are trying to run it the second time, you might not be able to get all of the perfect results (because of the auto-increment in the MariaDB). Please run `make stop` and `make start` first if you want to run the test suite again. ```bash make test ``` - Teardown or stop the container. This will also delete the Docker volume created and will also delete the created image. ```bash make stop ``` You're done! ## FAQ Some frequently asked questions that I found scattered on the Internet. Keep in mind that the answers are mostly subjective. **Q: Is this the right way to do Clean Architecture?** A: Nope. There are many ways to perform clean architecture - this example being one of them. Some projects might be better than this example. **Q: Why is authentication an implementation detail?** A: Authentication is an implementation detail because it does not interact with the use-case or the repository / interface layer. Authentication is a bit strange that it can be implemented in any other routes as a middleware. Keep in mind that this is my subjective opinion. **Q: Is this the recommended way to structure Fiber projects?** A: Nope. Just like any other Gophers, I recommend you to start your project by using a single `main.go` file. Some projects do not require complicated architectures. After you start seeing the need to branch out, I recommend you to [split your code based on functional responsibilities](https://rakyll.org/style-packages/). If you need an even more strict structure, then you can try to adapt Clean Architecture or any other architectures that you see fit, such as Onion, Hexagonal, etcetera. **Q: Is this only for Fiber?** A: Nope. You can simply adjust `handler.go` and `middleware.go` files in order to change the external interfaces / presenters and drivers layer to something else. You can use `net/http`, `gin-gonic`, `echo`, and many more. If you want to change or add your database, you just need to adjust the `repository.go` file accordingly. If you want to change your business logic, simply change the `service.go` file. As long as you the separation of concerns is done well, you should have no need to change a lot of things. **Q: Is this production-ready?** A: I try to make this as production-ready as possible 😉 ## Improvements Several further improvements that could be implemented in this project: - Add more tests and mocks, especially unit tests (Clean Architecture is the best for performing unit tests). - Add more API endpoints. - Add a caching mechanism to the repository layer, such as Redis. - Add transaction support. - Maybe try to integrate S3 backend to the repository layer (MinIO is a good choice). - Maybe add a `domain` folder in the `internal` package where we can leave the entities there? ## Discussion Feel free to create an issue in this repository (or maybe ask in Fiber's Discord Server) in order to discuss this together! ## References Thanks to articles and their writers that I have read and found inspiration in! - [Clean Architecture by Angad Sharma](https://medium.com/gdg-vit/clean-architecture-the-right-way-d83b81ecac6) - [Clean Architecture by Uncle Bob](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) - [Clean Architecture with Go by Elton Minetto](https://dev.to/eminetto/clean-architecture-using-golang-5791) - [Clean Architecture with Go Part 2 by Elton Minetto](https://dev.to/eminetto/clean-architecture-2-years-later-4een) - [Creating Clean Architecture using Go by @namkount](https://hackernoon.com/creating-clean-architecture-using-golang-9h5i3wgr) - [Dive to Clean Architecture with Go by Kenta Takeuchi](https://dev.to/bmf_san/dive-to-clean-architecture-with-golang-cd4) - [Go and Clean Architecture by Reshef Sharvit](https://itnext.io/golang-and-clean-architecture-19ae9aae5683) - [Go Microservices with Clean Architecture by Jin Feng](https://medium.com/@jfeng45/go-microservice-with-clean-architecture-application-design-68f48802c8f) - [Go Project Layout Repository](https://github.com/golang-standards/project-layout) - [Trying Clean Architecture on Go by Imam Tumorang](https://hackernoon.com/golang-clean-archithecture-efd6d7c43047) --- ## Docker + Nginx [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/docker-nginx-loadbalancer) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/docker-nginx-loadbalancer) ## Features - **Docker and Nginx** : Deploy in docker using 5 replicas and load balancer with Nginx - **Logger**: The application includes a request logger for monitoring HTTP requests. ## Endpoints | Name | Rute | Parameters | State | Protected | Method | |--------------|----------| ---------- | --------- | --------- |--------| | Hello | /hello | No | Completed | No | GET | ## Getting Started To get a local copy up and running, follow these steps: 1. Clone the repository to your local machine. 2. Navigate to the project directory. 3. Build the Docker image with docker compose 4. Run the Docker compose composition ```bash docker compose up --build ``` 5. Access the application at `http://localhost:8080/hello`. --- ## Dummy JSON Proxy # Simple Fiber Proxy Server [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/dummyjson) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/dummyjson) This is a basic Go application using the Fiber framework to create a proxy server. The server listens on port 3000 and has a single route (`GET /proxy`) that accepts an optional `?url=` query parameter, fetches data from that URL, and forwards it to the client. Without the parameter it falls back to a default upstream. > **Run this locally only.** The `url` parameter is forwarded to the HTTP client without validation, so anyone who can reach the route can make the server fetch arbitrary addresses on its behalf, including internal ones. That is a [server-side request forgery](https://owasp.org/API-Security/editions/2023/en/0xa7-server-side-request-forgery/) proxy. The example is kept minimal on purpose; before exposing anything like it, restrict the allowed schemes and hosts and reject private and loopback ranges. ## Prerequisites Ensure you have the following installed: - Go 1.25 or newer, required by Fiber v3 - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/dummyjson ``` 2. Install dependencies: ```sh go get ``` ## Running the Application Start the server: ```sh go run main.go ``` The server listens on port 3000. ## Usage Call the route without parameters to use the default upstream: ```sh curl "http://localhost:3000/proxy" ``` Pass a `url` query parameter to override it: ```sh curl "http://localhost:3000/proxy?url=https://dummyjson.com/products/2" ``` Either way the server fetches the data from the external service and forwards the response to the client. ### Error Handling - Returns 500 Internal Server Error if anything goes wrong during the fetch. - Returns the same status code as the external service if it is not 200 OK. ## References - [Fiber Documentation](https://docs.gofiber.io) - [DummyJSON](https://dummyjson.com) --- ## Email Verification Service # Email Verification Service with Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/email-verification) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/email-verification) A clean architecture based email verification service that generates and validates verification codes. ## Features - Clean Architecture implementation - In-memory verification code storage - SMTP email service integration - Code generation and hashing - Configurable code expiration - Thread-safe operations ## Project Structure ``` email-verification/ ├── api/ │ └── handlers/ # HTTP handlers ├── application/ # Application business logic ├── domain/ # Domain models and interfaces ├── infrastructure/ # External implementations │ ├── code/ # Code generation │ ├── email/ # SMTP service │ └── repository/ # Data storage └── config/ # Configuration ``` ## Configuration The application is configured via environment variables. Copy `.env.example` to `.env` and fill in your SMTP credentials: ```bash cp .env.example .env ``` | Variable | Required | Default | Description | |-------------|----------|----------------|------------------------------| | `SMTP_HOST` | No | `smtp.gmail.com` | SMTP server hostname | | `SMTP_PORT` | No | `587` | SMTP server port | | `SMTP_USER` | **Yes** | — | SMTP username / email address | | `SMTP_PASS` | **Yes** | — | SMTP password or app-password | The application exits with a fatal error on startup if `SMTP_USER` or `SMTP_PASS` are not set. ## API Endpoints | Method | URL | Description | |--------|----------------------------|--------------------------------| | POST | /verify/send/:email | Send verification code | | POST | /verify/check/:email/:code | Verify the received code | ## Example Usage 1. Send verification code: ```bash curl -X POST http://localhost:3000/verify/send/user@example.com ``` 2. Verify code: ```bash curl -X POST http://localhost:3000/verify/check/user@example.com/123456 ``` ## Response Examples Success: ```json { "message": "Code verified successfully" } ``` Error: ```json { "error": "invalid code" } ``` ## How to Run 1. Copy `.env.example` to `.env` and set your SMTP credentials. 2. Export the environment variables and run the application: ```bash export $(cat .env | xargs) go run main.go ``` ## Dependencies - [Fiber v3](https://github.com/gofiber/fiber) - Go 1.23+ --- ## Entgo ORM (MySQL) # Example ent ORM for fiber with MySQL [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/ent-mysql) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/ent-mysql) A sample program how to connect ent ORM ## How to start (If no ent dir) Execute command first ```bash go run -mod=mod entgo.io/ent/cmd/ent new Book ``` go to `./ent/schema/book.go` and add fields(you want) to Book Schema ```go // Fields of the Book. func (Book) Fields() []ent.Field { return []ent.Field{ field.String("title").NotEmpty(), field.String("author").NotEmpty(), } } ``` Execute command ```bash go generate ./ent ``` ### Endpoints | Method | URL | Description | |--------|-------------|-----------------| | GET | /book | All Books Info | | GET | /book/:id | One Book Info | | POST | /create | One Book Add | | PUT | /update/:id | One Book Update (reads fields from query params) | | DELETE | /delete/:id | One Book Delete | --- ## Entgo Sveltekit # Todo Application [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/entgo-sveltekit) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/entgo-sveltekit) ![image](https://github.com/ugurkorkmaz/gofiber-recipes/assets/40540244/08c6ee52-724a-4cf4-8352-9cf6f5b007ef) This Todo application is a full-stack project built using Sveltekit, Tailwind CSS, Fiber, Entgo, and SQLite. It showcases the construction of a monolithic architecture for a full-stack application. ## Run the Project To run the project, follow these steps: 1. Execute the following command to run all the necessary commands for building and running the application: ```bash go run ./bin all ``` 2. Once the build process is complete, you can start the application by running: ```bash ./app ``` ## Available Commands The following commands are available to manage the project: | Command | Description | | --- | --- | | `go-run` | Run the Golang project. | | `go-build` | Build the Golang project. | | `go-test` | Run tests for the Golang project. | | `svelte-run` | Run the SvelteKit project. | | `svelte-build` | Build the SvelteKit project. | | `generate-ent` | Generate entity files. | | `all` | Run all commands (`generate-ent`, `svelte-build`, `go-test`, `go-build`). | ## Usage To use this application, run the following command: ```bash go run ./bin ``` API Routes ---------- The Go Fiber application provides the following API routes: | Method | Endpoint | Handler Function | Description | | --- | --- | --- | --- | | GET | /api/v1/todo/list | todoHandler.GetAllTodos | Get a list of all todos | | GET | /api/v1/todo/get/:id | todoHandler.GetTodoByID | Get a specific todo by its ID | | POST | /api/v1/todo/create | todoHandler.CreateTodo | Create a new todo | | PUT | /api/v1/todo/update/:id | todoHandler.UpdateTodoByID | Update an existing todo by its ID | | DELETE | /api/v1/todo/delete/:id | todoHandler.DeleteTodoByID | Delete a todo by its ID | Go Dependencies --------------- - **Go Modules:** Go's built-in package manager used to manage dependencies for Go projects. - **Entgo:** A Golang Object Relational Mapping (ORM) tool used to define and generate database schemas. - **Fiber:** A fast and minimalist web framework for Golang. - **Sqlite:** A small, lightweight, embedded SQL database engine. Npm Dependencies ---------------- - **SvelteKit:** A JavaScript framework used to build modern web applications. - **Tailwind CSS:** A fast and customizable CSS styling library. Can be used in SvelteKit projects. ---------------- Author: [@ugurkorkmaz](https://github.com/ugurkorkmaz) --- ## SvelteKit and Tailwind CSS Project This is a SvelteKit project that utilizes Tailwind CSS for styling. SvelteKit is a framework for building modern web applications, and Tailwind CSS is a utility-first CSS framework. Together, they provide a powerful combination for creating responsive and visually appealing web interfaces. ## Available Scripts The following scripts are available in the project's `package.json` file: | Script | Description | | ------------- | ---------------------------------------------------------------------------------------- | | `dev` | Starts the development server and hot-reloads the application for a seamless development experience. | | `build` | Builds the project for production, generating optimized and minified files. | | `preview` | Starts a server to preview the production-ready build locally before deployment. | | `check` | Runs the Svelte compiler and type-checker to validate the project's TypeScript configuration. | | `check:watch` | Similar to `check`, but watches for changes and performs continuous type-checking. | ## Usage To use the available scripts, you need to have Node.js and Npm (or Pnpm) installed on your system. Follow these steps: 1. Install the project dependencies by running the following command in the project's root directory: ```bash npm install ``` or ```bash pnpm install ``` 2. Once the installation is complete, you can run the desired script using the following command: ```bash npm run (code) ``` or ```bash pnpm run (code) ``` Replace `(code)` with one of the available scripts mentioned in the table above. 3. The corresponding action will be executed, and you can see the output in the terminal. Please note that specific configurations and additional steps might be required depending on your project setup or requirements. Refer to the project documentation for more information. --- ## Envoy External Authorization # Fiber as an Envoy External Authorization HTTP Service [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/envoy-extauthz) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/envoy-extauthz) One way of extending the popular [Envoy](https://www.envoyproxy.io) proxy is by developing an [external authorization service](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto). This example illustrates using `fiber` and the `keyauth` middleware as an authorization service for a front proxy (the configuration could also be used for an L2 / Sidecar proxy). See `authz`. It also uses `fiber` as a sample upstream service, with the following endpoints. See `app`. ## Architecture ``` Client │ │ HTTP request (port 8000) ▼ Envoy (front-envoy) │ ├──► AuthZ service (fiber_authz :1337) │ Checks x-api-key header via keyauth middleware. │ Returns 200 OK → Envoy forwards request upstream. │ Returns 403 Forbidden → Envoy rejects request immediately. │ └──► App service (fiber_app) — only reached when AuthZ approves Serves /health (unprotected) and /api/resource (protected). ``` All three services run in the same Docker network (`envoymesh`). Envoy is the sole ingress point; the app service is never exposed directly. ## Prerequisites - [Docker](https://docs.docker.com/get-docker/) with Compose plugin (or `docker-compose` v1) ## Endpoints | Name | Rute | Protected | Method | | --------- | ------------- | --------- | ------ | | Health | /health | No | GET | | Resource | /api/resource | Yes | GET | ## Run `docker-compose up --build -d` ## Test | Name | Command | Status | | --------------- | ----------------------------------------------------------------- | ------ | | Not protected | `curl localhost:8000/health -i` | 200 | | Missing API key | `curl localhost:8000/api/resource -i` | 403 | | Invalid API key | `curl localhost:8000/api/resource -i -H "x-api-key: invalid-key"` | 403 | | Valid API key | `curl localhost:8000/api/resource -i -H "x-api-key: valid-key"` | 200 | ## Stop `docker-compose down` --- ## File Server # File Server Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/file-server) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/file-server) This project demonstrates how to set up a simple file server in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/file-server ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the file server at `http://localhost:3000`. ## Example Here is an example `main.go` file for the Fiber application serving static files: ```go 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 "files" directory app.Get("/*", static.New("./files")) log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Golang Documentation](https://golang.org/doc/) --- ## Firebase Authentication # Go Fiber Firebase Authentication Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/firebase-auth) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/firebase-auth) This example demonstrates how to protect Fiber routes with Firebase Authentication by verifying Firebase ID tokens. ## Requirements - A Firebase project with Authentication enabled - A Google Service Account credential JSON file (download from Firebase Console → Project Settings → Service Accounts) ## Setting Up Copy `example.env` to `.env` and set the path to your service account credential file: ``` GOOGLE_SERVICE_ACCOUNT=path/to/serviceAccountKey.json ``` ## Start ```bash go run main.go ``` ## Endpoints | Method | Path | Auth required | Description | |--------|-----------------|---------------|--------------------------------------| | GET | /salut | No | Public greeting (French) | | POST | /ciao | No | Public greeting (Italian) | | GET | /salanthe | No | Public greeting (Sinhalese) | | GET | /api/hello | Yes | Protected greeting (English) | | GET | /api/ayubowan | Yes | Protected greeting with user claims | ## curl Examples ### Public endpoint ```bash curl http://localhost:3001/salut ``` ### Protected endpoint — obtain a Firebase ID token first, then: ```bash curl -H "Authorization: Bearer " \ http://localhost:3001/api/hello ``` ```bash curl -H "Authorization: Bearer " \ http://localhost:3001/api/ayubowan ``` --- ## Firebase Functions # Deploying GoFiber Application to Firebase Functions [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/firebase-functions) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/firebase-functions) Welcome to this step-by-step guide on deploying a GoFiber application to Firebase Functions. If you’re looking to leverage the power of GoFiber, a fast and lightweight web framework for Go, and host your application on Firebase, you’re in the right place. In this tutorial, we’ll walk through the process of setting up your GoFiber app to run seamlessly on Firebase Functions. ## Prerequisites 1. Go installed on your machine. 2. Firebase CLI installed. 3. A Firebase project created. 4. Firestore and Cloud Functions enabled. ## Create a GoFiber App Start by initializing your GoFiber application. Use the following commands in your terminal: ```bash go mod init example.com/GofiberFirebaseBoilerplate ``` ## Server Configuration Create a server file `(src/server.go)` with a `CreateServer` function that sets up your GoFiber server. ```go package src import ( "example.com/GofiberFirebaseBoilerplate/src/routes" "github.com/gofiber/fiber/v3" ) func CreateServer() *fiber.App { version := "v1.0.0" app := fiber.New(fiber.Config{ ServerHeader: "Gofiber Firebase Boilerplate", AppName: "Gofiber Firebase Boilerplate " + version, }) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Gofiber Firebase Boilerplate " + version) }) routes.New().Setup(app) return app } ``` ## Routes Configuration Now that your GoFiber application is initialized, let’s delve into setting up and configuring routes. This section is crucial for defining how your application handles incoming requests. Open the `src/routes/routes.go` file to manage your routes. ```go package routes import ( "example.com/GofiberFirebaseBoilerplate/src/database" "example.com/GofiberFirebaseBoilerplate/src/models" "example.com/GofiberFirebaseBoilerplate/src/repositories" "github.com/gofiber/fiber/v3" ) type Routes struct { mainRepository *repositories.MainRepository } func New() *Routes { db := database.NewConnection() return &Routes{mainRepository: &repositories.MainRepository{DB: db}} } func (r *Routes) Setup(app *fiber.App) { app.Post("message", r.insertMessage) } func (r *Routes) insertMessage(c fiber.Ctx) error { var body models.MessageInputBody if err := c.Bind().JSON(&body); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } if err := r.mainRepository.InsertMessage(&body); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(fiber.Map{"id": body}) } ``` ## Database Configuration Configure your Firestore database connection in the `src/database/database.go` file. Authentication uses Application Default Credentials (ADC), which are provided automatically by Cloud Functions / Cloud Run. For local development, run `gcloud auth application-default login`. ```go package database import ( "context" "log" "cloud.google.com/go/firestore" firebase "firebase.google.com/go" ) // NewConnection initializes a Firestore client using Application Default Credentials (ADC). // On Cloud Functions / Cloud Run, ADC is provided automatically by the runtime environment. // For local development, run: gcloud auth application-default login func NewConnection() *firestore.Client { ctx := context.Background() // No explicit credentials option needed — ADC resolves credentials automatically. app, err := firebase.NewApp(ctx, nil) if err != nil { log.Fatalf("functions.init: NewApp %v\n", err) } db, err := app.Firestore(ctx) if err != nil { log.Fatalf("functions.init: Database init : %v\n", err) } return db } ``` ## Repository Pattern Implement the repository pattern in the `src/repositories/main.repository.go` file to interact with Firestore. This file includes an example of inserting a message into the database. ```go package repositories import ( "context" "cloud.google.com/go/firestore" "example.com/GofiberFirebaseBoilerplate/src/models" "github.com/google/uuid" ) type MainRepository struct { DB *firestore.Client } func (r *MainRepository) InsertMessage(body *models.MessageInputBody) error { id := uuid.New().String() _, err := r.DB.Collection("messages").Doc(id).Set(context.Background(), body) return err } ``` ## Model Definition Define a message input model in src/models/message_input_body.go to structure the data you'll be working with. ```go package models type MessageInputBody struct { From string `json:"from"` To string `json:"to"` Message string `json:"message"` } ``` ## Functions for Cloud Integration In `functions.go`, convert Google Cloud Function requests to Fiber and route them to your application. This file includes functions to facilitate the integration of Google Cloud Functions and GoFiber. ```go package app import ( "net/http" "github.com/gofiber/fiber/v3" adaptor "github.com/gofiber/fiber/v3/middleware/adaptor" ) // CloudFunctionRouteToFiber route cloud function http.Handler to *fiber.App // Internally, google calls the function with the /execute base URL func CloudFunctionRouteToFiber(fiberApp *fiber.App, w http.ResponseWriter, r *http.Request) { adaptor.FiberApp(fiberApp)(w, r) } ``` ## Main Application Entry In `main.go`, initialize your GoFiber app and start the server. This file also includes an exported Cloud Function handler for deployment. ```go package app import ( "fmt" "net/http" "strings" "example.com/GofiberFirebaseBoilerplate/src" "github.com/gofiber/fiber/v3" ) var app *fiber.App func init() { app = src.CreateServer() } // Start start Fiber app with normal interface func Start(addr string) error { if -1 == strings.IndexByte(addr, ':') { addr = ":" + addr } return app.Listen(addr) } // ServerFunction Exported http.HandlerFunc to be deployed to as a Cloud Function func ServerFunction(w http.ResponseWriter, r *http.Request) { CloudFunctionRouteToFiber(app, w, r) } ``` ## Development For local development, utilize the `cmd/main.go` file. If you prefer hot reloading, the `.air.toml` configuration file is included for use Air. ## cmd/main.go ```go package main import ( "log" "os" app "example.com/GofiberFirebaseBoilerplate" ) func main() { port := "3001" if envPort := os.Getenv("PORT"); envPort != "" { port = envPort } if err := app.Start(port); err != nil { log.Fatalf("app.Start: %v\n", err) } } ``` ## .air.toml ```go root = "." testdata_dir = "testdata" tmp_dir = "tmp" [build] args_bin = [] bin = "./tmp/main" cmd = "go build -o ./tmp/main ./cmd" delay = 1000 exclude_dir = ["assets", "tmp", "vendor", "testdata"] exclude_file = [] exclude_regex = ["_test.go"] exclude_unchanged = false follow_symlink = false full_bin = "" include_dir = [] include_ext = ["go", "tpl", "tmpl", "html"] include_file = [] kill_delay = "0s" log = "build-errors.log" poll = false poll_interval = 0 post_cmd = [] pre_cmd = [] rerun = false rerun_delay = 500 send_interrupt = false stop_on_error = false [color] app = "" build = "yellow" main = "magenta" runner = "green" watcher = "cyan" [log] main_only = false time = false [misc] clean_on_exit = false [screen] clear_on_rebuild = false keep_scroll = true ``` ## Deployment Deploy your Cloud Function using the following commands, replacing `` with your Firebase project ID: ```bash gcloud config set project gcloud functions deploy MyCloudFunction --runtime go122 --trigger-http ``` ## Conclusion Congratulations! You’ve successfully configured and deployed a GoFiber application on Firebase Functions. This powerful combination allows you to build fast and efficient serverless applications. Experiment further with GoFiber features and Firebase integrations to unlock the full potential of your serverless architecture. Happy coding! ## Medium Post https://medium.com/@kmltrk07/how-to-deploy-gofiber-app-to-firebase-functions-8d4d537a4464 --- ## Firebase GCloud # Deploy Fiber to Google Cloud with Firebase [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/gcloud) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/gcloud) Examples on how to run an application using Fiber on Google Cloud. ## Prerequisites - [gcloud CLI](https://cloud.google.com/sdk/docs/install) installed and authenticated (`gcloud auth login`) - A Google Cloud project created and billing enabled - Required APIs enabled: Cloud Run, App Engine, or Cloud Functions (depending on deployment method) - Set your project: `gcloud config set project [YOUR_PROJECT_ID]` ## Running Locally * Run on the command line: ``` go run cmd/main.go ``` ## Deploy using Google Cloud Run This step will build a Docker Image, publish to Google Cloud Registry and deploy on Cloud Run Managed enviroment. [![Run on Google Cloud](https://storage.googleapis.com/cloudrun/button.svg)](https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=https://github.com/gofiber/recipes&cloudshell_working_dir=gcloud) After deploying the server on Cloud Run, you can get it's url on GCP Console ([link](https://console.cloud.google.com/run)) and select the service `gcloud-fiber` that we just deployed. Them copy the URL will look like `https://{project-id}-{some-random-hash-string}.a.run.app`. Or you can do it manually with those steps: * Run on the command line: ``` export GCLOUD_PROJECT=[YOUR_PROJECT_ID] gcloud builds submit --tag gcr.io/$GCLOUD_PROJECT/gcloud-fiber . gcloud beta run deploy --platform managed --image gcr.io/$GCLOUD_PROJECT/gcloud-fiber ``` ## Deploy using Google App Engine This step will deploy the app to Google App Engine Standard Go enviroment. The app configuration and additional configurations can be tweaked on the `app.yaml` file. * Run on the command line: ``` gcloud app deploy ``` ## Deploy using Google Cloud Function This step will deploy a HTTP Cloud Function using Go enviroment. You can use the `deploy.sh` script. Just edit your project id on it. For the Cloud Functions env, Google enforces us to deploy a function that is a `http.HandlerFunc`, so on the file `functions.go` there is a workaround to reroute the HTTP call to the Fiber app instance. * Run on the command line: ``` gcloud functions deploy MyCloudFunction --runtime go122 --trigger-http ``` --- ## Google Cloud Firebase # Deploy Fiber to Google Cloud with Firebase [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/gcloud-firebase) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/gcloud-firebase) Examples on how to run an application using Fiber on Google Cloud and connecting to Firebase Realtime Database. ## Running Locally * Run on the command line: ``` go run cmd/main.go ``` ## Deploy using Google Cloud Run This step will build a Docker Image, publish to Google Cloud Registry and deploy on Cloud Run Managed enviroment. Just follow the steps and fill the `GCP_PROJECT` variable with your Google Cloud Platform project ID. That variable is needed to connect to Firebase. [![Run on Google Cloud](https://storage.googleapis.com/cloudrun/button.svg)](https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=https://github.com/gofiber/recipes&cloudshell_working_dir=gcloud-firebase) After deploying the server on Cloud Run, you can get it's url on GCP Console ([link](https://console.cloud.google.com/run)) and select the service `gcloud-fiber-firebase` that we just deployed. Then copy the URL. It will look like `https://{project-id}-{some-random-hash-string}.a.run.app`. Or you can do it manually with those steps: * Run on the command line: ``` export GCLOUD_PROJECT=[YOUR_PROJECT_ID] gcloud builds submit --tag gcr.io/$GCLOUD_PROJECT/gcloud-fiber-firebase . gcloud beta run deploy --platform managed --image gcr.io/$GCLOUD_PROJECT/gcloud-fiber-firebase \ --set-env-vars GCP_PROJECT=$GCLOUD_PROJECT ``` ## Deploy using Google App Engine This step will deploy the app to Google App Engine Standard Go enviroment. The app configuration and additional configurations can be tweaked on the `app.yaml` file. * Run on the command line: ``` gcloud app deploy ``` ## Deploy using Google Cloud Function This step will deploy a HTTP Cloud Function using Go enviroment. You can use the `deploy.sh` script. Just edit your project id on it. For the Cloud Functions env, Google enforces us to deploy a function that is a `http.HandlerFunc`, so on the file `functions.go` there is a workaround to reroute the HTTP call to the Fiber app instance. * Run on the command line: ``` gcloud functions deploy HeroesAPI --runtime go122 --trigger-http ``` --- ## GeoIP # GeoIP Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/geoip) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/geoip) This recipe demonstrates how to build a GeoIP lookup service with [Fiber](https://github.com/gofiber/fiber). It proxies requests to the [ip-api.com](http://ip-api.com) JSON API and caches responses for 10 minutes using Fiber's built-in cache middleware. > **Note:** This recipe depends on the free [ip-api.com](http://ip-api.com) service. The free tier is limited to **1000 requests per minute** from a single IP address. For higher traffic, consider a paid plan or a self-hosted alternative such as [geoip-maxmind](../geoip-maxmind/). ## Prerequisites - Go 1.21+ - Internet access (ip-api.com is called at runtime — no local database required) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/geoip ``` 2. Install dependencies: ```sh go get ``` ## Running the Application ```sh go run main.go ``` The server starts on port `3000` by default. Set the `PORT` environment variable to override: ```sh PORT=8080 go run main.go ``` Open `http://localhost:3000` in a browser to use the web UI. ## Example Look up geolocation data for an IP address via the `/geo` endpoint: ```sh curl "http://localhost:3000/geo?ip=178.62.56.160" ``` Example response: ```json { "status": "success", "country": "United Kingdom", "countryCode": "GB", "region": "ENG", "regionName": "England", "city": "London", "zip": "EC1A", "lat": 51.5085, "lon": -0.1257, "timezone": "Europe/London", "isp": "DigitalOcean, LLC", "org": "DigitalOcean, LLC", "as": "AS14061 DigitalOcean, LLC", "query": "178.62.56.160" } ``` Omit the `ip` query parameter to look up the caller's own IP address: ```sh curl "http://localhost:3000/geo" ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [ip-api.com Documentation](http://ip-api.com/docs) - [ip-api.com Rate Limits](http://ip-api.com/docs/api:json#usage_limits) --- ## GeoIP + MaxMind # GeoIP (with MaxMind databases) [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/geoip-maxmind) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/geoip-maxmind) This is an alternative method to resolve IP addresses to real-world location data using MaxMind GeoLite2 City databases. ## Prerequisites Before you run this, you must first download a database from the MaxMind website - https://dev.maxmind.com/geoip/geoip2/geolite2/. To do this, you need to register for a free MaxMind account. > **Note:** The GeoLite2 database is provided by MaxMind under the [GeoLite2 End User License Agreement](https://www.maxmind.com/en/geolite2/eula). A free MaxMind account is required to download the database file. The database you need to download is the one with the edition ID `GeoLite2-City`. Place it in this folder and run ``` go run . ``` ## Usage Make a request to `http://127.0.0.1:3000/geo/178.62.56.160`, for example. You can omit an IP address to use your current IP address, or replace to use another. If the IP address is invalid, a HTTP 400 is returned. The response fields can be modified from the `ipLookup` struct, found in the `handlers/handlers.go` file. ### Example response ```json { "City": { "GeoNameID": 2643743, "Names": { "de": "London", "en": "London", "es": "Londres", "fr": "Londres", "ja": "ロンドン", "pt-BR": "Londres", "ru": "Лондон", "zh-CN": "伦敦" } }, "Country": { "IsoCode": "GB" }, "Location": { "AccuracyRadius": 50 } } ``` --- ## GORM # GORM Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/gorm) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/gorm) This is a sample program demonstrating how to use GORM as an ORM to connect to a SQLite database with the Fiber web framework. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/gorm ``` 2. Install dependencies: ```sh go mod tidy ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3000`. ## Endpoints | Method | URL | Description | | ------ | ---------------- | -------------------------- | | GET | /api/v1/book | Retrieves all books | | GET | /api/v1/book/:id | Retrieves a book by ID | | POST | /api/v1/book | Creates a new book | | DELETE | /api/v1/book/:id | Deletes a book | ## Example Requests ### Get All Books ```sh curl -X GET http://localhost:3000/api/v1/book ``` ### Get Book by ID ```sh curl -X GET http://localhost:3000/api/v1/book/1 ``` ### Create a New Book ```sh curl -X POST http://localhost:3000/api/v1/book -d '{"title": "New Book", "author": "Author Name"}' -H "Content-Type: application/json" ``` ### Delete a Book ```sh curl -X DELETE http://localhost:3000/api/v1/book/1 ``` --- ## GORM MySQL # GORM MySQL Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/gorm-mysql) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/gorm-mysql) This is a sample program demonstrating how to use GORM as an ORM to connect to a MySQL database with the Fiber web framework. ## Prerequisites - Go 1.25 or higher - MySQL database - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/gorm-mysql ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Configure the database connection via the `DB_DSN` environment variable: ```sh export DB_DSN="user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" ``` If `DB_DSN` is not set, the application falls back to the default DSN above. ## Running the Application 1. Run the application: ```sh go run app.go ``` 2. The server will start on `http://localhost:3000`. ## Endpoints | Method | URL | Description | | ------ | --------- | -------------------------- | | GET | /hello | Returns a hello message | | GET | /allbooks | Retrieves all books | | GET | /book/:id | Retrieves a book by ID | | POST | /book | Creates a new book | | PUT | /book/:id | Updates an existing book | | DELETE | /book/:id | Deletes a book | ## Example Requests ### Get All Books ```sh curl -X GET http://localhost:3000/allbooks ``` ### Get Book by ID ```sh curl -X GET http://localhost:3000/book/1 ``` ### Create a New Book ```sh curl -X POST http://localhost:3000/book -d '{"title": "New Book", "author": "Author Name"}' -H "Content-Type: application/json" ``` ### Update a Book ```sh curl -X PUT http://localhost:3000/book/1 -d '{"title": "Updated Book", "author": "Updated Author"}' -H "Content-Type: application/json" ``` ### Delete a Book ```sh curl -X DELETE http://localhost:3000/book/1 ``` --- ## GORM + PostgreSQL # GORM with PostgreSQL Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/gorm-postgres) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/gorm-postgres) This project demonstrates how to set up a Go application using the Fiber framework with GORM and PostgreSQL. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - [GORM](https://gorm.io/) package - PostgreSQL ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/gorm-postgres ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Set up PostgreSQL and create a database: ```sh createdb go-db ``` 4. Configure the database connection via the `DB_DSN` environment variable: ```sh export DB_DSN="host=localhost user=postgres password='' dbname=go-db port=5432 sslmode=disable" ``` If `DB_DSN` is not set, the application falls back to the default DSN above. ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the application at `http://localhost:3000`. ## Endpoints | Method | URL | Description | | ------ | ---------- | -------------------------- | | GET | /hello | Returns a hello message | | GET | /allbooks | Retrieves all books | | GET | /book/:id | Retrieves a book by ID | | POST | /book | Creates a new book | | PUT | /book/:id | Updates an existing book | | DELETE | /book/:id | Deletes a book | ## Example Requests ### Get All Books ```sh curl -X GET http://localhost:3000/allbooks ``` ### Get Book by ID ```sh curl -X GET http://localhost:3000/book/1 ``` ### Create a New Book ```sh curl -X POST http://localhost:3000/book \ -d '{"title": "New Book", "author": "Author Name"}' \ -H "Content-Type: application/json" ``` ### Update a Book ```sh curl -X PUT http://localhost:3000/book/1 \ -d '{"title": "Updated Book", "author": "Updated Author"}' \ -H "Content-Type: application/json" ``` ### Delete a Book ```sh curl -X DELETE http://localhost:3000/book/1 ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [GORM Documentation](https://gorm.io/docs/) - [PostgreSQL Documentation](https://www.postgresql.org/docs/) --- ## Graceful shutdown # Graceful shutdown in Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/graceful-shutdown) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/graceful-shutdown) ``` fiberRecipes/graceful-shutdown on graceful-shutdown (f0834df) [?] via 🐹 v1.15.2 took 4s ❯ go run graceful-shutdown ┌───────────────────────────────────────────────────┐ │ Fiber v3.0.0 │ │ http://127.0.0.1:3000 │ │ │ │ Handlers ............. 2 Threads ............. 8 │ │ Prefork ....... Disabled PID .............. 2540 │ └───────────────────────────────────────────────────┘ ^CGracefully shutting down... Running cleanup tasks... ``` This shows how to implement a graceful shutdown with Fiber and the `os/signal` package. ## Explanation This example relies on the use of channels, a data type in Go that allows you to send and receive data to/from specific places in an application (read more about them [here](https://tour.golang.org/concurrency/2)). A channel is created, and registered with `signal.Notify` so that when the program receives an interrupt (for example, when `CTRL+C` is pressed), a notification is sent to the channel. Once this is received, `app.Shutdown` is called to close all active connections and return from `app.Listen`. After this point, cleanup functions can be run and the program eventually quits. --- ## GraphQL # GraphQL Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/graphql) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/graphql) This project demonstrates how to set up a GraphQL server in a Go application using the Fiber framework and the [graphql-go](https://github.com/graphql-go/graphql) library. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - [graphql-go](https://github.com/graphql-go/graphql) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/graphql ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. The server listens on port `9090` and exposes a single endpoint at `/` that accepts both GET and POST requests. ## Usage ### GET request Pass the GraphQL query as a URL-encoded `query` parameter: ```sh curl 'http://localhost:9090/?query=query%7Bhello%7D' ``` ### POST request Send the query as a JSON body with `Content-Type: application/json`: ```sh curl 'http://localhost:9090/' \ --header 'content-type: application/json' \ --data-raw '{"query":"query{hello}"}' ``` Both return a JSON response like: ```json {"data":{"hello":"world"}} ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [graphql-go Documentation](https://github.com/graphql-go/graphql) - [GraphQL Documentation](https://graphql.org/) --- ## gRPC # Example for fiber as a client to gRPC server. [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/grpc) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/grpc) A sample program to showcase fiber as a client to a gRPC server. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/grpc ``` 2. Install dependencies: ```sh go mod tidy ``` ## Running the Application 1. Run the gRPC server: ```sh go run server/main.go ``` 2. Run the Fiber client: ```sh go run client/main.go ``` 3. The server will start on `http://localhost:3000`. ## Endpoints | Method | URL | Return value | | ------ | ------------- | ------------ | | GET | /add/:a/:b | a + b | | GET | /mult/:a/:b | a \* b | ### Output ```bash -> curl http://localhost:3000/add/33445/443234 {"result":"476679"} -> curl http://localhost:3000/mult/33445/443234 {"result":"14823961130"} ``` ## Regenerating Proto Files If you modify `proto/service.proto`, regenerate the Go bindings with one of the following methods: ### Using protoc Install the required tools: ```sh go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest ``` Then regenerate: ```sh protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ proto/service.proto ``` ### Using buf Install buf: https://buf.build/docs/installation ```sh buf generate ``` ## Additional Information gRPC (gRPC Remote Procedure Calls) is a high-performance, open-source universal RPC framework initially developed by Google. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, load balancing, and more. For more information, visit the [official gRPC documentation](https://grpc.io/docs/). --- ## Hello World # Hello World Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/hello-world) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/hello-world) This project demonstrates a simple "Hello, World!" application using the Fiber framework in Go. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/hello-world ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the application at `http://localhost:3000`. ## Example Here is an example `main.go` file for the Fiber application: ```go package main import ( "log" "github.com/gofiber/fiber/v3" ) func main() { // Fiber instance app := fiber.New() // Routes app.Get("/", hello) // Start server log.Fatal(app.Listen(":3000")) } // Handler func hello(c fiber.Ctx) error { return c.SendString("Hello, World 👋!") } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Golang Documentation](https://golang.org/doc/) --- ## Heroku # Heroku Deployment Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/heroku) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/heroku) This project demonstrates how to deploy a Go application using the Fiber framework on Heroku. > **Note:** Heroku removed its free tier in November 2022. A paid plan is required to deploy applications. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/heroku ``` 2. Install dependencies: ```sh go get ``` 3. Log in to Heroku: ```sh heroku login ``` 4. Create a new Heroku application: ```sh heroku create ``` 5. Build the binary and add a `Procfile` to the project directory: ```sh go build -o bin/main . ``` `Procfile`: ``` web: bin/main ``` 6. Deploy the application to Heroku: ```sh git add . git commit -m "Deploy to Heroku" git push heroku master ``` ## Running the Application 1. Open the application in your browser: ```sh heroku open ``` ## Example Here is an example `main.go` file for the Fiber application: ```go package main import ( "log" "os" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, Heroku!") }) log.Fatal(app.Listen(":" + getPort())) } func getPort() string { port := os.Getenv("PORT") if port == "" { port = "3000" } return port } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Heroku Documentation](https://devcenter.heroku.com/) --- ## Hexagonal Architecture # A Hexagonal Software Architecture in Golang and MongoDB [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/hexagonal) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/hexagonal) This project presents a simple product catalogue microservice to demonstrate the principles of a hexagonal software architecture. The microservice exposes a RESTful API that allows consuming applications to perform CRUD operations on a product catalogue. The microservice is developed in Golang, and the product catalogue data is persisted in a MongoDB repository. ![Hexagonal Architecture](Hexagonal-Arch.png) ## Prerequisites - Go 1.25 or higher - MongoDB - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/hexagonal ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Configure the MongoDB connection in the `config.yaml` file: ```yaml database: url: "your_mongodb_uri" db: "your_db_name" timeout: 30 ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:8080`. ## Endpoints | Method | URL | Description | | ------ | ---------------- | -------------------------------- | | GET | /api/v1/products | Retrieves all products | | GET | /api/v1/product/:id | Retrieves a product by ID | | POST | /api/v1/product | Creates a new product | | PUT | /api/v1/product/:id | Updates an existing product | | DELETE | /api/v1/product/:id | Deletes a product | ## Example Requests ### Get All Products ```sh curl -X GET http://localhost:8080/api/v1/products ``` ### Get Product by ID ```sh curl -X GET http://localhost:8080/api/v1/product/1 ``` ### Create a New Product ```sh curl -X POST http://localhost:8080/api/v1/product -d '{"name": "New Product", "price": 100}' -H "Content-Type: application/json" ``` ### Update a Product ```sh curl -X PUT http://localhost:8080/api/v1/product/1 -d '{"name": "Updated Product", "price": 150}' -H "Content-Type: application/json" ``` ### Delete a Product ```sh curl -X DELETE http://localhost:8080/api/v1/product/1 ``` ## Hexagonal Architecture Hexagonal architecture, also known as ports and adapters architecture, is a design pattern used to create loosely coupled application components that can be easily connected to their software environment by means of ports and adapters. This architecture allows an application to be equally driven by users, programs, automated tests, or batch scripts, and to be developed and tested in isolation from its eventual runtime devices and databases. ## Additional Information For more information on hexagonal architecture, you can refer to the following resources: - [Hexagonal Architecture](https://alistair.cockburn.us/hexagonal-architecture/) - [Hexagonal Architecture in Golang](https://medium.com/@matryer/hexagonal-architecture-in-go-2b5e0df2d8f8) --- ## HTTPS with PKCS12 TLS # HTTPS with PKCS12 TLS Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/https-pkcs12-tls) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/https-pkcs12-tls) This project demonstrates how to set up an HTTPS server with PKCS12 TLS in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - PKCS12 certificate file (`cert.p12`) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/https-pkcs12-tls ``` 2. Install dependencies: ```sh go get ``` 3. Place your PKCS12 certificate file (`server.p12`) in the `security/` directory. 4. Optionally set the PKCS12 password via environment variable (defaults to `changeit`): ```sh export PKCS12_PASSWORD=yourpassword ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the application at `https://localhost:443`. ## Example Here is an example of how to set up an HTTPS server with PKCS12 TLS in a Fiber application: ```go package main import ( "crypto" "crypto/tls" "log" "os" "github.com/gofiber/fiber/v3" "golang.org/x/crypto/pkcs12" ) func main() { path := "./security/server.p12" password := os.Getenv("PKCS12_PASSWORD") if password == "" { password = "changeit" } // Read and decode PKCS12 file pkcs12Data, err := os.ReadFile(path) if err != nil { log.Fatal(err) } key, cert, err := pkcs12.Decode(pkcs12Data, password) if err != nil { log.Fatal(err) } tlsCert := tls.Certificate{ Certificate: [][]byte{cert.Raw}, PrivateKey: key.(crypto.PrivateKey), Leaf: cert, } config := &tls.Config{Certificates: []tls.Certificate{tlsCert}} app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("This page is being served over TLS using a PKCS12 store type!") }) ln, err := tls.Listen("tcp", ":443", config) if err != nil { log.Fatal(err) } log.Fatal(app.Listener(ln)) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [TLS in Go](https://golang.org/pkg/crypto/tls/) - [PKCS12 in Go](https://pkg.go.dev/golang.org/x/crypto/pkcs12) --- ## HTTPS with TLS # HTTPS with TLS Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/https-tls) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/https-tls) This project demonstrates how to set up an HTTPS server with TLS in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - TLS certificates (self-signed or from a trusted CA) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/https-tls ``` 2. Install dependencies: ```sh go get ``` 3. Generate a self-signed certificate and key: ```sh mkdir -p certs openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout certs/ssl.key -out certs/ssl.cert \ -subj "/CN=localhost" ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the application at `https://localhost:443`. ## Example Here is an example of how to set up an HTTPS server with TLS in a Fiber application: ```go package main import ( "crypto/tls" "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString(c.Protocol()) // => https }) // Create tls certificate cer, err := tls.LoadX509KeyPair("certs/ssl.cert", "certs/ssl.key") if err != nil { log.Fatal(err) } config := &tls.Config{Certificates: []tls.Certificate{cer}} // Create custom listener ln, err := tls.Listen("tcp", ":443", config) if err != nil { log.Fatal(err) } // Start server with https/ssl enabled on http://localhost:443 log.Fatal(app.Listener(ln)) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [TLS in Go](https://golang.org/pkg/crypto/tls/) --- ## I18n(I18n) # Fiber with i18n [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/i18n) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/i18n) This is a quick example of how to use [nicksnyder/go-i18n](https://github.com/nicksnyder/go-i18n) package to translate your Fiber application into multiple languages. ## Demo - Run Fiber application; - Open `http://127.0.0.1:3000/?unread=1` and see: ```bash Hello Bob I have 1 unread email. Bob has 1 unread email. ``` - Next, go to `http://127.0.0.1:3000/?unread=4` and see pluralization of your message: ```bash Hello Bob I have 4 unread emails. Bob has 4 unread emails. ``` - OK. Try translation of other languages, just add `&lang=es` (or `&lang=ru`) query to the URL: ```bash Hola Bob Tengo 4 correos electrónicos no leídos Bob tiene 4 correos electrónicos no leídos ``` ## Getting Started ```bash go run main.go ``` Set `ENV=development` to enable template hot-reload: ```bash ENV=development go run main.go ``` ## File Structure ``` i18n/ ├── lang/ │ ├── active.en.toml # English translations (default) │ ├── active.es.toml # Spanish translations │ └── active.ru.toml # Russian translations ├── templates/ │ └── index.html # HTML template ├── main.go # Application entry point ├── go.mod └── go.sum ``` ## go-i18n docs - [Translating a new language](https://github.com/nicksnyder/go-i18n#translating-a-new-language); - [Translating a new messages (updating)](https://github.com/nicksnyder/go-i18n#translating-new-messages); --- ## JWT(Jwt) # Fiber with JWT [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/jwt) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/jwt) This example demonstrates how to use JSON Web Tokens (JWT) for authentication in a Fiber application. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/jwt ``` 2. Install dependencies: ```sh go mod tidy ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3000`. ## Endpoints | Method | URL | Description | | ------ | ------------- | -------------------------- | | POST | /login | Authenticates a user and returns a JWT | | GET | /restricted | Accesses a restricted route with JWT | ## Example Requests ### Login ```sh curl -X POST http://localhost:3000/login -d '{"username": "user", "password": "pass"}' -H "Content-Type: application/json" ``` ### Access Restricted Route ```sh curl -X GET http://localhost:3000/restricted -H "Authorization: Bearer " ``` ## Postman Collection You can find Postman examples [here](https://www.getpostman.com/collections/0e83876e0f2a0c8ecd70). --- ## Kubernetes # Kubernetes Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/k8s) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/k8s) This project demonstrates how to deploy a Go application using the Fiber framework on a Kubernetes cluster. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - Docker - Kubernetes - kubectl - [Minikube](https://minikube.sigs.k8s.io/docs/start/) (for local development) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/k8s ``` 2. Install dependencies: ```sh go get ``` 3. Build the Docker image: ```sh docker build -t fiber-k8s-example . ``` 4. Start Minikube (if using Minikube): ```sh minikube start ``` 5. Deploy the application to Kubernetes: ```sh kubectl apply -f my-service.yaml ``` ## Running the Application 1. Check the status of the pods: ```sh kubectl get pods ``` 2. Forward the port to access the application: ```sh kubectl port-forward svc/fiber-k8s-example 3000:3000 ``` 3. Access the application at `http://localhost:3000`. ## Example Here is an example `main.go` file for the Fiber application: ```go package main import ( "context" "log" "os/signal" "syscall" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, Kubernetes!") }) app.Get("/healthz", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() go func() { if err := app.Listen(":3000"); err != nil { log.Printf("Listen error: %v\n", err) } }() <-ctx.Done() log.Println("Gracefully shutting down...") if err := app.ShutdownWithContext(ctx); err != nil { log.Printf("Shutdown error: %v\n", err) } } ``` Here is an example `Dockerfile` for the application: ```Dockerfile FROM golang:1.25-alpine AS builder WORKDIR /app COPY go.mod ./ COPY go.sum ./ RUN go mod download COPY *.go ./ RUN go build -o /fiber-k8s-example EXPOSE 3000 CMD ["/fiber-k8s-example"] ``` Here is an example `my-service.yaml` file for deploying the application to Kubernetes: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: fiber-k8s-example spec: replicas: 2 selector: matchLabels: app: fiber-k8s-example template: metadata: labels: app: fiber-k8s-example spec: containers: - name: fiber-k8s-example image: fiber-k8s-example:latest ports: - containerPort: 3000 readinessProbe: httpGet: path: /healthz port: 3000 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 3000 initialDelaySeconds: 10 periodSeconds: 20 --- apiVersion: v1 kind: Service metadata: name: fiber-k8s-example spec: type: NodePort selector: app: fiber-k8s-example ports: - protocol: TCP port: 3000 targetPort: 3000 nodePort: 30001 ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Kubernetes Documentation](https://kubernetes.io/docs/) - [Docker Documentation](https://docs.docker.com/) --- ## Todo App + Auth + GORM + Testcontainers # Todo App with Auth using GORM and Testcontainers [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/local-development-testcontainers) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/local-development-testcontainers) This project demonstrates a Todo application with authentication using GORM and Testcontainers. The database is a Postgres instance created using the GoFiber's [Testcontainers Service module](https://github.com/gofiber/contrib/testcontainers). The instance is reused across multiple runs of the application, allowing to develop locally without having to wait for the database to be ready. When using the `air` command to run the application, the database is automatically started alongside the Fiber application, and it's automatically stopped when the air command is interrupted. ## Prerequisites Ensure you have the following installed and available in your `GOPATH`: - Golang - [Air](https://github.com/air-verse/air) for hot reloading ## Installation 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/local-development-testcontainers ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh air ``` ## Environment Variables Create a `.env` file in the root directory and add the following variables: ```shell # PORT returns the server listening port # default: 8000 PORT= # DB returns the name of the PostgreSQL database # default: postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable # TOKENKEY returns the jwt token secret TOKENKEY= # TOKENEXP returns the jwt token expiration duration. # Should be time.ParseDuration string. Source: https://golang.org/pkg/time/#ParseDuration # default: 10h TOKENEXP= # TESTCONTAINERS_RYUK_DISABLED disables the Ryuk container, to avoid removing the database container when the application is stopped. # default: true TESTCONTAINERS_RYUK_DISABLED=true ``` --- ## Memgraph # Fiber and Memgraph [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/memgraph) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/memgraph) This is a cookbook recipe for setting up Fiber backend and Memgraph database. 🚀 ## Prerequisites Go is an obvious prerequisite. Make sure it is installed and configured properly. After that you need two Go packages: Fiber and Neo4j driver for Go. You can install them with the following commands: ``` go get -u github.com/gofiber/fiber/v3 go get github.com/neo4j/neo4j-go-driver/v5 ``` ## Run Memgraph The easiest way to run Memgraph is to use Docker. Once docker is installed on your machine, you can run Memgraph with the following command: ``` docker run –name memgraph -it -p 7687:7687 -p 7444:7444 -p 3000:3000 -v mg_lib:/var/lib/memgraph memgraph/memgraph-platform ``` ## Run the recipe After you have installed all the prerequisites, you can run the recipe with the following command: ``` cd memgraph go run ./main.go ``` This will do the following: 1. Connect Fiber backend to Memgraph database 2. Generate mock data to populate the database 3. Define two request handlers: one for getting the graph and one for getting developer nodes ## Test the recipe Once Fiber app is running, you can test the recipe by sending a GET request to the following endpoints: ``` http://localhost:3000/graph http://localhost:3000/developer/Andy ``` ## Additional resources For extra information use the documentation on the following links: - Fiber: https://docs.gofiber.io/ - Memgraph: https://memgraph.com/docs --- ## MinIO # MinIO File Upload & Download Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/minio) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/minio) This example demonstrates a simple Go Fiber application that includes modules for uploading both single and multiple files, as well as downloading files from MinIO. Each module provides REST API endpoints for file upload and retrieval, serving as a foundation for applications requiring file storage and access. ## Prerequisites Ensure you have the following installed: - [Go](https://golang.org/dl/): (version 1.22 or higher) installed - [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/): for running a local MinIO instance - [Git](https://git-scm.com/downloads) ## Project Structure - `single/main.go`: Example for uploading and downloading a single file to/from MinIO. - `multiple/main.go`: Example for uploading multiple files to MinIO and downloading files from MinIO. - `go.mod`: Go module file managing project dependencies. ## Getting Started ### 1. Clone the Repository Clone the repository and navigate to the example directory: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/minio ``` ### 2. Start MinIO with Docker Compose A `docker-compose.yml` is provided for running a local MinIO instance: ```bash docker-compose up -d ``` This starts MinIO on port `9000` (API) and `9001` (web console). Access the console at [http://localhost:9001](http://localhost:9001) with credentials `minioadmin` / `minioadmin`. To stop MinIO: ```bash docker-compose down ``` ### 3. Install Dependencies Use Go’s module system to install dependencies: ```bash go mod download ``` ## Running the Examples ### Uploading and Downloading a Single File 1. Go to the `single` directory: ```bash cd single ``` 2. Start the application: ```bash go run main.go ``` 3. Upload a file using `curl` or `Postman`: ```bash curl -F "document=@/path/to/your/file" http://localhost:3000/upload ``` 4. Download the file by specifying its name in the request: ```bash curl -O http://localhost:3000/file/ ``` ### Uploading Multiple Files and Downloading Files 1. Go to the `multiple` directory: ```bash cd multiple ``` 2. Start the application: ```bash go run main.go ``` 3. Upload multiple files using `curl` or `Postman`: ```bash curl -F "documents=@/path/to/your/file1" -F "documents=@/path/to/your/file2" http://localhost:3000/upload ``` 4. Download a file by specifying its name in the request. ```bash curl -O http://localhost:3000/file/ ``` ## Code Overview ### `single/main.go` - Defines routes to handle a single file upload and download. - Includes error handling for file validation, MinIO connection, and bucket management. ### `multiple/main.go` - Handles uploading multiple files in a single request and allows for file downloads. - Validates each file and provides detailed responses for both successful and failed uploads. ## Conclusion This example offers a approach for managing file uploads and downloads with Go Fiber and MinIO. It can be expanded to support additional features, such as adding metadata, handling large files, or restricting access to files. ## References - [Fiber Documentation](https://docs.gofiber.io) - [Fiber storage](https://github.com/gofiber/storage) - [MinIO Documentation](https://min.io/docs/) --- ## MongoDB # MongoDB Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/mongodb) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/mongodb) This project demonstrates how to connect to a MongoDB database in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - MongoDB - [MongoDB Go Driver](https://github.com/mongodb/mongo-go-driver) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/mongodb ``` 2. Install dependencies: ```sh go get ``` 3. Start MongoDB using Docker: ```sh docker-compose up -d ``` 4. (Optional) Set the `MONGO_URI` environment variable. Defaults to `mongodb://localhost:27017/fiber_test`: ```sh export MONGO_URI="mongodb://localhost:27017/fiber_test" ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to connect to a MongoDB database in a Fiber application: ```go package main import ( "context" "log" "time" "github.com/gofiber/fiber/v3" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func main() { // MongoDB connection ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017")) if err != nil { log.Fatal(err) } defer client.Disconnect(context.Background()) // Fiber instance app := fiber.New() // Routes app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, MongoDB!") }) // Start server log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [MongoDB Documentation](https://docs.mongodb.com) - [MongoDB Go Driver Documentation](https://pkg.go.dev/go.mongodb.org/mongo-driver) --- ## Monitoring with Apitally [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/monitoring-with-apitally) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/monitoring-with-apitally) This project showcases a simple REST API built with the Fiber framework in Go, featuring monitoring and request logging via Apitally. [Apitally](https://apitally.io/fiber) is a lightweight monitoring and analytics tool that helps developers track API usage, performance, and errors with minimal setup. ## Prerequisites Ensure you have Golang installed. ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/monitoring-with-apitally ``` 2. Install dependencies: ```sh go get ``` 3. Obtain a client ID from [Apitally](https://apitally.io/fiber) by signing up and creating a new app in the dashboard. ## Running the application 1. Start the application: ```sh APITALLY_CLIENT_ID=your-client-id go run main.go ``` 2. Make requests to the API: ```sh curl -X GET -H "Authorization: Bearer d7e123f5a2b9c4e8d6a7b2c1f5e9d3a4" http://localhost:3000/v1/books ``` ## Dashboard The Apitally dashboard will show the requests you've made to the API. It provides detailed insights into the API's usage, errors, and performance. Individual requests can be inspected in the request log. You can also set up custom alerts. ![Apitally screenshots](https://assets.apitally.io/screenshots/overview.png) ## References - [Apitally Documentation](https://docs.apitally.io/setup-guides/fiber) - [Fiber Documentation](https://docs.gofiber.io) --- ## Multiple Ports # Multiple Ports Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/multiple-ports) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/multiple-ports) This project demonstrates how to run a Go application using the Fiber framework on multiple ports. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/multiple-ports ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to run a Fiber application on multiple ports: ```go package main import ( "log" "sync" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ports := []string{":3000", ":3001"} var wg sync.WaitGroup for _, port := range ports { wg.Add(1) go func(p string) { defer wg.Done() if err := app.Listen(p); err != nil { log.Printf("Error starting server on port %s: %v", p, err) } }(port) } wg.Wait() } ``` In this example: - The application listens on multiple ports (`:3000` and `:3001`). - A `sync.WaitGroup` is used to wait for all goroutines to finish. ## References - [Fiber Documentation](https://docs.gofiber.io) --- ## MySQL # MySQL Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/mysql) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/mysql) This project demonstrates how to connect to a MySQL database in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - MySQL - [Go MySQL Driver](https://github.com/go-sql-driver/mysql) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/mysql ``` 2. Install dependencies: ```sh go get ``` 3. Set up your MySQL database and update the connection string in the code. ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to connect to a MySQL database in a Fiber application: ```go package main import ( "database/sql" "log" "github.com/gofiber/fiber/v3" _ "github.com/go-sql-driver/mysql" ) func main() { // Database connection dsn := "username:password@tcp(127.0.0.1:3306)/dbname" db, err := sql.Open("mysql", dsn) if err != nil { log.Fatal(err) } defer db.Close() // Fiber instance app := fiber.New() // Routes app.Get("/", func(c fiber.Ctx) error { var greeting string err := db.QueryRow("SELECT 'Hello, World!'").Scan(&greeting) if err != nil { return err } return c.SendString(greeting) }) // Start server log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [MySQL Documentation](https://dev.mysql.com/doc/) - [Go MySQL Driver Documentation](https://pkg.go.dev/github.com/go-sql-driver/mysql) --- ## Neo4j # Neo4j Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/neo4j) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/neo4j) This project demonstrates how to connect to a Neo4j database in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - Neo4j - [Neo4j Go Driver](https://github.com/neo4j/neo4j-go-driver) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/neo4j ``` 2. Install dependencies: ```sh go get ``` 3. Set up your Neo4j database and export connection settings: ```sh export NEO4J_URI=neo4j://localhost:7687 export NEO4J_USER=neo4j export NEO4J_PASSWORD=password export NEO4J_DATABASE=movies ``` Or start Neo4j via Docker Compose: ```sh docker compose up -d ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to connect to a Neo4j database in a Fiber application: ```go package main import ( "context" "fmt" "log" "os" "github.com/gofiber/fiber/v3" "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) type Movie struct { Title string `json:"title"` Tagline string `json:"tagline"` Released int64 `json:"released"` Director string `json:"director"` } func envOrDefault(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func main() { uri := envOrDefault("NEO4J_URI", "neo4j://localhost:7687") user := envOrDefault("NEO4J_USER", "neo4j") password := envOrDefault("NEO4J_PASSWORD", "password") database := envOrDefault("NEO4J_DATABASE", "movies") driver, err := neo4j.NewDriverWithContext(uri, neo4j.BasicAuth(user, password, "")) if err != nil { log.Fatal(err) } defer func() { if closeErr := driver.Close(context.Background()); closeErr != nil { log.Printf("failed to close neo4j driver: %v", closeErr) } }() if err := driver.VerifyConnectivity(context.Background()); err != nil { log.Fatal(err) } app := fiber.New() app.Post("/movie", func(c fiber.Ctx) error { movie := new(Movie) if err := c.Bind().Body(movie); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } ctx := c.Context() session := driver.NewSession(ctx, neo4j.SessionConfig{ DatabaseName: database, AccessMode: neo4j.AccessModeWrite, }) defer func() { _ = session.Close(ctx) }() query := `CREATE (n:Movie {title: $title, tagline: $tagline, released: $released, director: $director})` _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { _, runErr := tx.Run(ctx, query, map[string]any{ "title": movie.Title, "tagline": movie.Tagline, "released": movie.Released, "director": movie.Director, }) return nil, runErr }) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(movie) }) app.Get("/movie/:title", func(c fiber.Ctx) error { title := c.Params("title") ctx := c.Context() session := driver.NewSession(ctx, neo4j.SessionConfig{ DatabaseName: database, AccessMode: neo4j.AccessModeRead, }) defer func() { _ = session.Close(ctx) }() query := `MATCH (n:Movie {title: $title}) RETURN n.title AS title, n.tagline AS tagline, n.released AS released, n.director AS director` result, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) { res, runErr := tx.Run(ctx, query, map[string]any{"title": title}) if runErr != nil { return nil, runErr } if !res.Next(ctx) { if res.Err() != nil { return nil, res.Err() } return nil, nil } row := res.Record().AsMap() return Movie{ Title: row["title"].(string), Tagline: row["tagline"].(string), Released: row["released"].(int64), Director: row["director"].(string), }, nil }) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } if result == nil { return c.SendStatus(fiber.StatusNotFound) } return c.Status(fiber.StatusOK).JSON(result) }) log.Fatal(app.Listen(":3000")) } ``` ### curl Examples **Create a movie:** ```sh curl -X POST http://localhost:3000/movie \ -H "Content-Type: application/json" \ -d '{"title":"The Matrix","tagline":"Welcome to the Real World","released":1999,"director":"Lana Wachowski"}' ``` **Get a movie by title:** ```sh curl http://localhost:3000/movie/The%20Matrix ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Neo4j Documentation](https://neo4j.com/docs/) - [Neo4j Go Driver Documentation](https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j) --- ## OAuth2 # OAuth2 (GitHub) [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/oauth2) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/oauth2) This project demonstrates how to implement GitHub OAuth2 authentication in a GoFiber application. ## Prerequisites - Go 1.21+ - A [GitHub OAuth App](https://github.com/settings/developers) - Set **Authorization callback URL** to `http://localhost:8080/oauth/redirect` ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/oauth2 ``` 2. Copy the example env file and fill in your credentials: ```sh cp .env.example .env ``` 3. Install dependencies: ```sh go mod download ``` ## Running the Application ```sh go run app.go ``` Then open `http://localhost:8080` in your browser. ## Environment Variables Create a `.env` file in the root directory (see `.env.example`): ```shell # GitHub OAuth2 App credentials CLIENT_ID=your_github_client_id CLIENT_SECRET=your_github_client_secret ``` ## OAuth2 Flow ``` Browser → GET /oauth/begin → generates CSRF state, stores in session → redirects to https://github.com/login/oauth/authorize GitHub → GET /oauth/redirect?code=...&state=... → validates CSRF state → exchanges code for access token via GitHub API → stores token in session → redirects to /welcome.html GET /protected → OAUTHProtected middleware checks session token ``` ## Example: GitHub OAuth2 token exchange ```go // POST https://github.com/login/oauth/access_token // with client_id, client_secret, and code // Response: // {"access_token":"gho_...","token_type":"bearer","scope":""} ``` ## References - [GitHub OAuth Apps documentation](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps) - [GoFiber documentation](https://docs.gofiber.io) --- ## Google OAuth2 # Fiber with Google OAuth2 [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/oauth2-google) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/oauth2-google) This example demonstrates how to implement Google OAuth2 authentication in a Fiber application. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/oauth2-google ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Obtain OAuth credentials from [Google Developers Console](https://console.developers.google.com/). 4. Create a `.env` file in the root directory and add your Google OAuth credentials: ```env APP_PORT=3300 GOOGLE_CLIENT_ID=your_client_id GOOGLE_CLIENT_SECRET=your_client_secret GOOGLE_REDIRECT_URL=http://localhost:3300/api/auth/google/callback ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3300`. ## Endpoints | Method | URL | Description | | ------ | ---------------------------- | ------------------------------------------------ | | GET | /api/ | Redirects to Google login URL | | GET | /api/auth/google/callback | Handles Google OAuth2 callback and returns user's email | ## Example Requests ### Redirect to Google Login ```sh curl -X GET http://localhost:3300/api/ ``` ### Google OAuth2 Callback ```sh curl -X GET http://localhost:3300/api/auth/google/callback?state=state&code=code ``` ## Security ### OAuth2 State Validation (CSRF Protection) The login handler generates a random `state` parameter and stores it in an `HttpOnly`, `Secure`, `SameSite=Lax` cookie before redirecting to Google. The callback handler compares the `state` query parameter returned by Google against the stored cookie value and returns `403 Forbidden` if they do not match. This prevents [Cross-Site Request Forgery (CSRF)](https://owasp.org/www-community/attacks/csrf) attacks against the OAuth2 flow. ## Packages Used - [Godotenv](https://github.com/joho/godotenv) - [Fiber](https://github.com/gofiber/fiber) - [OAuth2](https://github.com/golang/oauth2) --- ## OpenAPI # OpenAPI Documentation [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/openapi) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/openapi) This project demonstrates how to add OpenAPI 3 documentation to a Go application using [Huma](https://github.com/danielgtaylor/huma). This project got inspired by the [swagger recipe](https://github.com/gofiber/recipes/tree/master/swagger). ## Prerequisites Ensure you have the following installed: - Golang ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/openapi ``` 2. Download Go modules: ```sh go mod tidy ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the API Documentation: Open your browser and navigate to `http://localhost:3000/docs`. 3. OpenAPI Specs: - OpenAPI 3.1 JSON: `http://localhost:3000/openapi.json`. - OpenAPI 3.1 YAML: `http://localhost:3000/openapi.yaml`. - OpenAPI 3.0.3 JSON: `http://localhost:3000/openapi-3.0.json`. - OpenAPI 3.0.3 YAML: `http://localhost:3000/openapi-3.0.yaml`. 4. Generating TypeScript schema: ```sh npx openapi-typescript http://localhost:3000/openapi.json -o schema.ts ``` ## Example Here is a minimal example of adding huma to a existing Fiber codebase: ### `routes.go` ```go import ( ... "github.com/gofiber/fiber/v2" "github.com/danielgtaylor/huma/v2" "github.com/danielgtaylor/huma/v2/adapters/humafiber" ) func New() *fiber.App { app := fiber.New() api := humafiber.New(app, huma.DefaultConfig("Book API", "1.0.0")) // app.Get("/books", handlers.GetAllBooks) // 👈 your existing code huma.Get(api, "/books", handlers.GetAllBooks) // 👈 huma version return app } ``` ### `handlers/book.go` ```go // func GetAllBooks(c *fiber.Ctx) error {} // 👈 your existing code // 👇 huma version func GetAllBooks(ctx context.Context, _ *struct{}) (*GetAllBooksResponse, error) { return &GetAllBooksResponse{Body: books}, nil } ``` ## Enhancing Documentation You can use `huma.Register` to add more information to the OpenAPI specification, such as descriptions with Markdown, examples, tags, and more. ```go // huma.Get(group, "/books/{id}", handlers.GetBookByID) huma.Register(api, huma.Operation{ OperationID: "get-book-by-id", Method: http.MethodGet, Path: "/book/{id}", Summary: "Get a book", Description: "Get a book by book ID.", Tags: []string{"Books"}, }, handlers.GetBookByID) ``` ## References - [Huma Documentation](https://github.com/danielgtaylor/huma) - [Huma Fiber Adapter](https://huma.rocks/features/bring-your-own-router) - [Enhancing Documentation](https://huma.rocks/tutorial/your-first-api/#enhancing-documentation) --- ## Optional Parameter # Optional Parameter Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/optional-parameter) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/optional-parameter) This project demonstrates how to handle optional parameters in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/optional-parameter ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to handle optional parameters in a Fiber application: ```go package main import ( "log" "strconv" "github.com/gofiber/fiber/v3" ) func main() { // user list users := [...]string{"Alice", "Bob", "Charlie", "David"} // Fiber instance app := fiber.New() // Route to profile app.Get("/:id?", func(c fiber.Ctx) error { id, err := strconv.Atoi(c.Params("id")) // transform id to array index if err != nil || id < 0 || id >= len(users) { return c.SendStatus(fiber.StatusNotFound) // invalid parameter returns 404 } return c.SendString("Hello, " + users[id] + "!") // custom hello message to user with the id }) // Start server log.Fatal(app.Listen(":3000")) } ``` In this example: - The `:id?` parameter in the route is optional. - If no valid `id` is provided, a `404 Not Found` is returned. - Valid `id` values (0-3) map to users Alice, Bob, Charlie, and David. ## References - [Fiber Documentation](https://docs.gofiber.io) --- ## Parsley # Fiber with Dependency Injection (via Parsley) [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/parsley) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/parsley) This example demonstrates integrating the [Parsley dependency injection framework](https://github.com/matzefriedrich/parsley) into a GoFiber web application. The goal is to showcase how dependency injection can create a clean, maintainable, and modular structure in your GoFiber projects. ## Prerequisites * Go 1.25+ ## Overview In this example, we use [Parsley](https://github.com/matzefriedrich/parsley) to: * **Bootstrap the application:** Set up and configure the Fiber app using Parsley’s DI container. * **Register dependencies:** Define and register services and route handlers with the DI container. * **Resolve dependencies:** Automatically resolve and inject them where needed. ### Key features * **Modular configuration:** Services are registered in modules, allowing for a clean separation of concerns. * **Automatic dependency injection:** Constructor-based dependency injection wires services together. * **Simplified route management:** Route handlers are registered and managed via the DI container, making it easy to extend and maintain. ## How it works * The `main` function bootstraps the application using Parsley’s `RunParsleyApplication` function. * Modules define how services (such as the Fiber app and route handlers) are registered and configured. * Route handlers are implemented as services that receive their dependencies (like the `Greeter` service) via constructor injection. The `Greeter` service is a simple example of how services can be injected and used within route handlers to handle requests. ## The recipe - step by step This guide demonstrates integrating the Parsley dependency injection framework with the GoFiber web framework. You can either clone the GoFiber recipes repository and navigate to the **parsley** example, or replicate each module while following the article: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/parsley ``` The main entry point of the application is in the `cmd/main.go`. ```go package main import ( "context" "github.com/gofiber/recipes/parsley-app/internal" "github.com/gofiber/recipes/parsley-app/internal/modules" "github.com/matzefriedrich/parsley/pkg/bootstrap" ) func main() { ctx := context.Background() // Runs a Fiber instance as a Parsley-enabled app bootstrap.RunParsleyApplication(ctx, internal.NewApp, modules.ConfigureFiber, modules.ConfigureGreeter) } ``` In this file, the `RunParsleyApplication` function bootstraps the application. It initializes the Parsley application context and configures the GoFiber server with the necessary services and route handlers. Parsley's `bootstrap` package is generic and could also be used with other web application frameworks; the glue is the `NewApp` method, representing a constructor function that must return a `bootstrap.Application` instance. The last parameter of the `RunParsleyApplication` function is an ellipsis parameter accepting `ModuleFunc` values representing service registration functions, which are invoked before calling the constructor function for `bootstrap.Application`. Here, the `ConfigureFiber` and `ConfigureGreeter` functions are specified; those are defined by the `modules` package. ### Configure and register the Fiber instance The `ConfigureFiber` function sets up the Fiber application and registers it as a singleton service within the Parsley framework: ```go package modules import ( "github.com/gofiber/fiber/v3" "github.com/matzefriedrich/parsley/pkg/registration" "github.com/matzefriedrich/parsley/pkg/types" ) var _ types.ModuleFunc = ConfigureFiber func ConfigureFiber(registry types.ServiceRegistry) error { registration.RegisterInstance(registry, fiber.Config{ AppName: "parsley-app-recipe", Immutable: true, }) registry.Register(newFiber, types.LifetimeSingleton) registry.RegisterModule(RegisterRouteHandlers) return nil } func newFiber(config fiber.Config) *fiber.App { return fiber.New(config) } ``` This configuration ensures that the Fiber instance is initialized and available for dependency injection. ### Define and register the application service(s) The `Greeter` service generates greeting messages based on input parameters. In the recipe example application, this service is a dependency required by the handler of the `say-hello` route. ```go package services import "fmt" type Greeter interface { SayHello(name string, polite bool) string } type greeter struct{} func (g *greeter) SayHello(name string, polite bool) string { if polite { return fmt.Sprintf("Good day, %s!\n", name) } return fmt.Sprintf("Hi, %s\n", name) } func NewGreeter() Greeter { return &greeter{} } ``` The `Greeter` service is registered by the `ConfigureGreeter` service registration module: ```go package modules import ( "github.com/gofiber/recipes/parsley-app/internal/services" "github.com/matzefriedrich/parsley/pkg/types" ) func ConfigureGreeter(registry types.ServiceRegistry) error { registry.Register(services.NewGreeterFactory, types.LifetimeTransient) return nil } ``` This setup allows the `Greeter` service to be injected wherever needed within the application. ### Implement and register route handlers Route handlers in this example are services that implement the `RouteHandler` interface, allowing them to register routes with the Fiber application. ```go package route_handlers import ( "strconv" "github.com/gofiber/recipes/parsley-app/internal/services" "github.com/gofiber/fiber/v3" ) type greeterRouteHandler struct { greeter services.Greeter } const defaultPoliteFlag = "true" func (h *greeterRouteHandler) Register(app *fiber.App) { app.Get("/say-hello", h.HandleSayHelloRequest) } func (h *greeterRouteHandler) HandleSayHelloRequest(ctx fiber.Ctx) error { name := ctx.Query("name") politeFlag := ctx.Query("polite", defaultPoliteFlag) polite, _ := strconv.ParseBool(politeFlag) msg := h.greeter.SayHello(name, polite) return ctx.Status(fiber.StatusOK).Send([]byte(msg)) } var _ RouteHandler = &greeterRouteHandler{} func NewGreeterRouteHandler(greeter services.Greeter) RouteHandler { return &greeterRouteHandler{ greeter: greeter, } } ``` This handler responds to GET requests at `/say-hello` with a greeting message, utilizing the `Greeter` service injected via the constructor function. ## Run the application To start the application, execute: ```sh go run ./cmd/main.go ``` Once running, you can test the `say-hello` endpoint via the browser, or from the terminal using `curl`. For this recipe, the default listening port is `5502`: ```sh curl http://localhost:5502/say-hello?name=YourName&polite=true ``` --- ## PostgreSQL # PostgreSQL Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/postgresql) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/postgresql) This project demonstrates how to connect to a PostgreSQL database in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - PostgreSQL ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/postgresql ``` 2. Install dependencies: ```sh go get ``` 3. Set up your PostgreSQL database and update the connection string in the code. ## Running the Application 1. Start the application: ```sh go run main.go ``` 2. Access the application at `http://localhost:3000`. ## Example Here is an example of how to connect to a PostgreSQL database in a Fiber application: ```go package main import ( "database/sql" "log" "github.com/gofiber/fiber/v3" _ "github.com/jackc/pgx/v5/stdlib" ) func main() { // Database connection connStr := "user=username dbname=mydb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { log.Fatal(err) } defer db.Close() // Fiber instance app := fiber.New() // Routes app.Get("/", func(c fiber.Ctx) error { var greeting string err := db.QueryRow("SELECT 'Hello, World!'").Scan(&greeting) if err != nil { return err } return c.SendString(greeting) }) // Start server log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [PostgreSQL Documentation](https://www.postgresql.org/docs/) - [pgx Driver Documentation](https://pkg.go.dev/github.com/jackc/pgx/v5) --- ## Prefork # Prefork Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/prefork) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/prefork) This project demonstrates how to use the `Prefork` feature in a Go application using the Fiber framework. Preforking can improve performance by utilizing multiple CPU cores. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/prefork ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to set up the `Prefork` feature in a Fiber application: ```go package main import ( "fmt" "log" "os" "github.com/gofiber/fiber/v3" ) func main() { // Print current process if fiber.IsChild() { fmt.Printf("[%d] Child\n", os.Getppid()) } else { fmt.Printf("[%d] Master\n", os.Getppid()) } // Fiber instance app := fiber.New() // Routes app.Get("/", hello) // Start server with prefork enabled log.Fatal(app.Listen(":3000", fiber.ListenConfig{EnablePrefork: true})) } // Handler func hello(c fiber.Ctx) error { return c.SendString("Hello, World!") } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Fiber Prefork Documentation](https://docs.gofiber.io/api/fiber#prefork) --- ## RabbitMQ # Fiber and RabbitMQ example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/rabbitmq) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/rabbitmq) ## Description This example demonstrates how to integrate [RabbitMQ](https://www.rabbitmq.com/) with a [Fiber](https://github.com/gofiber/fiber) HTTP server. The API exposes a `/send` endpoint that publishes messages to a RabbitMQ queue. A separate worker process consumes messages from the queue and prints them to the console. ## How it works ![Architecture diagram](https://user-images.githubusercontent.com/11155743/112727736-f8ca2200-8f34-11eb-8d40-12d9f381bd05.png) - The Fiber API server connects to RabbitMQ and exposes `GET /send?msg=`. - Each request publishes the `msg` query parameter as a message to the `TestQueue` queue. - The worker process subscribes to `TestQueue` and logs received messages. ## Prerequisites - [Go](https://golang.org/) 1.21+ - [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) ## Environment variables | Variable | Default | Description | |----------------|------------------------------------------|--------------------------| | `RABBITMQ_URL` | `amqp://user:password@localhost:5672/` | RabbitMQ connection URL | ## Setup ### Option A: Docker Compose (recommended) Start all services (RabbitMQ, worker, API) with a single command: ```bash docker compose up --build ``` ### Option B: Manual setup 1. Start RabbitMQ: ```bash make docker.network make docker.rabbitmq ``` 2. Wait ~30 seconds for RabbitMQ to be ready. 3. Start the worker (in a separate terminal): ```bash make docker.worker ``` 4. Run the API server: ```bash make run # or RABBITMQ_URL=amqp://user:password@localhost:5672/ go run main.go ``` ## Endpoints ### `GET /send` Publishes a message to the `TestQueue` RabbitMQ queue. | Parameter | Type | Required | Description | |-----------|--------|----------|----------------------| | `msg` | string | yes | Message to publish | **Success response** `200 OK`: ```json {"status": "message sent"} ``` **Error response** `400 Bad Request`: ```json {"error": "msg parameter required"} ``` ## curl examples Send a message: ```bash curl "http://127.0.0.1:3000/send?msg=Hello%20World" ``` Missing parameter (returns 400): ```bash curl "http://127.0.0.1:3000/send" ``` ## Worker output When a message is received, the worker prints: ```console 2021/03/27 16:32:35 Successfully connected to RabbitMQ instance 2021/03/27 16:32:35 [*] - Waiting for messages 2021/03/27 16:32:35 [*] - Run Fiber API server and go to http://127.0.0.1:3000/send?msg= 2021/03/27 16:33:24 Received message: Hello World ``` ## RabbitMQ management dashboard The RabbitMQ management UI is available at [http://localhost:15672](http://localhost:15672) (default credentials: `user` / `password`). ![RabbitMQ dashboard](https://user-images.githubusercontent.com/11155743/112728092-8fe3a980-8f36-11eb-9d79-be8eab26358b.png) --- ## React # React Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/react-router) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/react-router) A sample application to showcase serving React (with Router) with an almost bare Fiber. Hopefully, this application can be of use (as a reference or others) for those who wants to serve their client-side SPA with Fiber. ## Technologies - Go with Fiber - React with TypeScript and React Router - Docker ## Application - This application has three routes: `/`, `/react`, and a catch-all, 404 route. `/` will show the Fiber logo, `/react` will show the React logo, and the 404 route will show both logos. - As this application serves the frontend while backed by a server, the client-side routing will work well and will not cause any issue (unlike if you are running without a file server). You can type the URL route manually in the browser and it will still work and will render the accurate page, so no worries. - This is a simplified form of Create React App with TypeScript. With that being said, that's why there is no `manifest.json`, `logo512.png`, and other extra things like that. - I restructured the project structure to be a bit more modular by categorizing files to `assets`, `components`, and `styles`. I also made it so all of the CSS is loaded in `index.tsx` for easier seeing. - I also moved several dependencies to their appropriate places, such as `@types` and `test` in development dependencies instead of dependencies. ## Installation It is recommended that you use Docker to instantly run this application. After running the Docker application, please open `localhost:8080` in your browser. Make sure you are in the `react-router` folder before running these commands. ```bash docker build . -t react-router:latest docker run -d -p 8080:8080 react-router:latest ``` If you prefer doing things manually, then the installation steps are as follows: - Clone the repository by using `git clone git@github.com:gofiber/recipes.git`. - Switch to the application by using `cd recipes/react-router`. - Install npm dependencies by using `cd web && yarn install`. - Build frontend by using `yarn build`. - Run the Fiber application by using `go run cmd/react-router/main.go`. Don't forget to return to the main repository by using `cd ..` (assuming you are in `web` folder). - Open `localhost:8080` in your browser. --- ## Recover Middleware # Recover Middleware Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/recover) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/recover) This project demonstrates how to implement a recovery mechanism in a Go application using the Fiber framework's `Recover` middleware. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/recover ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to set up the `Recover` middleware in a Fiber application: ```go package main import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/recover" ) func main() { app := fiber.New() // Use the Recover middleware app.Use(recover.New()) app.Get("/", func(c fiber.Ctx) error { // This will cause a panic panic("something went wrong") }) app.Listen(":3000") } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Fiber Recover Middleware Documentation](https://docs.gofiber.io/api/middleware/recover) --- ## RSS Feed [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/rss-feed) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/rss-feed) This project demonstrates how to create an RSS feed in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/rss-feed ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example This recipe uses [Mustache templates](https://github.com/cbroglie/mustache) (via the `gofiber/template/mustache` engine) to render an RSS XML response. The template lives in `./xmls/example.xml`. **`xmls/example.xml`:** ```xml {{{Lang}}} {{{Title}}} {{{Greetings}}} ``` **`main.go`:** ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/mustache/v3" ) func main() { engineXML := mustache.New("./xmls", ".xml") if err := engineXML.Load(); err != nil { log.Fatal(err) } app := fiber.New() app.Get("/rss", func(c fiber.Ctx) error { // Set Content-Type to application/rss+xml c.Type("rss") // Render Mustache template with data return engineXML.Render(c, "example", fiber.Map{ "Lang": "en", "Title": "hello-rss", "Greetings": "Hello World", }) }) log.Fatal(app.Listen(":3000")) } ``` ### Testing with curl ```sh curl http://localhost:3000/rss ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Fiber Template - Mustache](https://github.com/gofiber/template/tree/master/mustache) - [cbroglie/mustache](https://pkg.go.dev/github.com/cbroglie/mustache) --- ## Seenode # Seenode Deployment Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/seenode) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/seenode) This project demonstrates how to deploy a Go application using the Fiber framework on Seenode. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package - [Seenode account](https://cloud.seenode.com) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/seenode ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Create a Seenode account and connect your repository: - Go to [Seenode Dashboard](https://cloud.seenode.com) - Create a new Web Service - Connect your Git repository 4. Configure deployment: - **Build Command**: `go build -o app main.go` - **Start Command**: `./app` 5. Deploy the application: ```sh git add . git commit -m "Deploy to Seenode" git push ``` ## Running the Application 1. Open the application in your browser using the provided Seenode URL. ## Example See `./main.go` for the full application code. It exposes: - `GET /` — welcome message - `GET /health` — health check, returns `{"status":"ok"}` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Seenode Documentation](https://seenode.com/docs/frameworks/go/fiber/) - [Seenode](https://seenode.com) --- ## Server Timing [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/server-timing) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/server-timing) This project demonstrates how to implement Server-Timing headers in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/server-timing ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to set up Server-Timing headers in a Fiber application: ```go package main import ( "fmt" "log" "time" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() app.Use(func(c fiber.Ctx) error { start := time.Now() err := c.Next() // dur value must be in milliseconds per W3C spec c.Append("Server-Timing", fmt.Sprintf("app;dur=%.2f", float64(time.Since(start).Microseconds())/1000.0)) return err }) app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) log.Fatal(app.Listen(":3000")) } ``` ### Testing with curl ```sh curl -i http://localhost:3000/ ``` Example response header: ``` Server-Timing: app;dur=2001.23 ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Server-Timing Header Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing) --- ## Sessions + SQLite3 # Sessions - SQLite3 [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/sessions-sqlite3) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/sessions-sqlite3) This example uses the SQLite3 storage package to persist user sessions. While the storage package can automatically create the sessions table at initialization, we create it manually to add an additional "u" column. This custom column serves several purposes: - Enables efficient querying of sessions by user identifier - Allows tracking of multiple sessions per user - Facilitates session cleanup for specific users The default table schema only stores session data and expiry, making it difficult to associate sessions with specific users. The "u" column solves this limitation. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/sessions-sqlite3 ``` 2. Install dependencies: ```sh go mod tidy ``` 3. Create the sessions table in SQLite3: ```sql CREATE TABLE sessions ( k TEXT PRIMARY KEY, v BLOB, e INTEGER, u TEXT ); ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3000`. ## Explanation This example uses the SQLite3 storage package to persist user sessions. The storage package can create the sessions table for you at initialization, but for the purpose of this example, the table is created manually with an additional "u" column to better query all user-related sessions. ## Security Notes - The UID passed from the front-end is used directly to identify the user. In production, **always validate the UID** server-side (e.g., verify credentials against your database) instead of trusting client-supplied values. - Session type assertions (`s.Get("uid").(string)`) are guarded with an `ok` check to prevent panics on malformed or missing session data. --- ## Socketio # WebSocket Chat Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/socketio) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/socketio) This example demonstrates how to create a simple chatroom using WebSockets. The chatroom supports multiple users and allows them to send messages to each other. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/socketio ``` 2. Install dependencies: ```sh go mod tidy ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3000`. ## Connecting to the WebSocket To connect to the WebSocket, use the following URL: ``` ws://localhost:3000/ws/ ``` ## Message Object Example Here is an example of a message object that can be sent between users: ```json { "from": "", "to": "", "data": "hello" } ``` --- ## Single Page Application (SPA) [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/spa) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/spa) This project demonstrates how to set up a Single Page Application (SPA) using React for the frontend and Go with the Fiber framework for the backend. ## Prerequisites Ensure you have the following installed: - Golang - Node.js - npm ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/spa ``` 2. Install frontend dependencies: ```sh cd frontend npm install ``` 3. Install backend dependencies: ```sh cd ../backend go get ``` ## Usage ### Building Frontend Assets 1. Build the frontend assets: ```sh cd frontend npm run build ``` 2. Watch frontend assets for changes: ```sh npm run dev ``` ### Running the Application 1. Start the Fiber backend application: ```sh cd backend go run main.go ``` ## Example Here is an example of how to serve a SPA with Fiber v3 using the `static` middleware: ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/static" ) func main() { app := fiber.New() // Serve Single Page Application on "/web". // The NotFoundHandler falls back to index.html so client-side routing works. app.Get("/web*", static.New("dist", static.Config{ NotFoundHandler: func(c fiber.Ctx) error { return c.SendFile("./dist/index.html") }, })) // Start server log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [React Documentation](https://reactjs.org/docs/getting-started.html) - [Tailwind CSS Documentation](https://tailwindcss.com/docs) - [Parcel Documentation](https://parceljs.org/docs) --- ## Sqlboiler # Fiber with sqlboiler [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/sqlboiler) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/sqlboiler) > #### 🎯 [Fiber](https://github.com/gofiber/fiber) + [Sqlboiler](https://github.com/aarondl/sqlboiler) Example ## 👀 Usage #### 1. Run Postgres ```bash $ docker compose build ``` ```bash $ docker compose up ``` #### 2. Wait 1-2 minutes ```console [+] Running 2/0 ✔ Network sqlboiler_default Created 0.0s ✔ Container postgres Created 0.0s Attaching to postgres postgres | postgres | PostgreSQL Database directory appears to contain a database; Skipping initialization postgres | postgres | 2023-09-22 01:09:46.453 UTC [1] LOG: starting PostgreSQL 16.0 (Debian 16.0-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit postgres | 2023-09-22 01:09:46.453 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 postgres | 2023-09-22 01:09:46.453 UTC [1] LOG: listening on IPv6 address "::", port 5432 postgres | 2023-09-22 01:09:46.454 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" postgres | 2023-09-22 01:09:46.461 UTC [30] LOG: database system was shut down at 2023-09-22 01:09:44 UTC postgres | 2023-09-22 01:09:46.468 UTC [1] LOG: database system is ready to accept connections ``` #### 3. You have to migrate the database. > ###### 🎯 It is a "database-first" ORM as opposed to "code-first" (like gorm/gorp). That means you must first create your database schema. > ###### 🎯 I used [golang-migrate](https://github.com/golang-migrate/migrate) to proceed with the migrate. ###### 1. Make Migration files ```bash $ migrate create -ext sql -dir ./migrations -seq create_initial_table ``` ```console sqlboiler/migrations/000001_create_initial_table.up.sql sqlboiler/migrations/000001_create_initial_table.up.sql ``` ###### 2. Migrate ```bash $ migrate -path migrations -database "postgresql://user:password@localhost:5432/fiber_demo?sslmode=disable" -verbose up ``` ```console 2023/09/22 20:00:00 Start buffering 1/u create_initial_table 2023/09/22 20:00:00 Read and execute 1/u create_initial_table 2023/09/22 20:00:00 Finished 1/u create_initial_table (read 24.693541ms, ran 68.30925ms) 2023/09/22 20:00:00 Finished after 100.661625ms 2023/09/22 20:00:00 Closing source and database ``` ###### 3. Rollback Migrate ```bash $ migrate -path migrations -database "postgresql://user:password@localhost:5432/fiber_demo?sslmode=disable" -verbose down ``` ```console 2023/09/22 20:00:00 Are you sure you want to apply all down migrations? [y/N] y 2023/09/22 20:00:00 Applying all down migrations 2023/09/22 20:00:00 Start buffering 1/d create_initial_table 2023/09/22 20:00:00 Read and execute 1/d create_initial_table 2023/09/22 20:00:00 Finished 1/d create_initial_table (read 39.681125ms, ran 66.220125ms) 2023/09/22 20:00:00 Finished after 1.83152475s ``` #### 4. Use sqlboiler ###### 1. Install ```bash # Go 1.25 and above: $ go install github.com/aarondl/sqlboiler/v4@latest $ go install github.com/aarondl/sqlboiler/v4/drivers/sqlboiler-psql@latest ``` ###### 2. Create a configuration file > ###### 🎯 The configuration file should be named sqlboiler.toml ###### Example ```toml output = "models" wipe = true no-tests = true add-enum-types = true [psql] dbname = "fiber_demo" host = "localhost" port = 5432 user = "user" pass = "password" schema = "schema" blacklist = ["migrations", "other"] ``` ###### 3. Create models > ###### 🎯 After creating a configuration file that points at the database we want to generate models for, we can invoke the sqlboiler command line utility. ```bash $ sqlboiler psql ``` ```text models/ ├── author.go ├── boil_queries.go ├── boil_table_names.go ├── boil_types.go ├── boil_view_names.go ├── post.go ├── schema_migrations.go ``` --- ## Sqlc # Fiber with sqlc [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/sqlc) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/sqlc) > #### 🎯 [fiber](https://github.com/gofiber/fiber) + [sqlc](https://github.com/sqlc-dev/sqlc) Example ## Usage #### 1. Run Postgres ```bash $ docker compose build ``` ```bash $ docker compose up ``` #### 2. Wait 1-2 minutes ```console [+] Running 2/0 ✔ Network sqlc_default Created 0.1s ✔ Container postgres Created 0.0s Attaching to postgres postgres | postgres | PostgreSQL Database directory appears to contain a database; Skipping initialization postgres | postgres | postgres | 2023-09-28 09:17:50.737 UTC [1] LOG: starting PostgreSQL 16.0 (Debian 16.0-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit postgres | 2023-09-28 09:17:50.737 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 postgres | 2023-09-28 09:17:50.737 UTC [1] LOG: listening on IPv6 address "::", port 5432 postgres | 2023-09-28 09:17:50.740 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" postgres | 2023-09-28 09:17:50.751 UTC [30] LOG: database system was shut down at 2023-09-28 08:50:35 UTC postgres | 2023-09-28 09:17:50.770 UTC [1] LOG: database system is ready to accept connections ``` #### 3. You have to migrate the database. > ##### 🎯 It is a "database-first" ORM as opposed to "code-first" (like gorm/gorp). That means you must first create your database schema. > ##### 🎯 I used [golang-migrate](https://github.com/golang-migrate/migrate) to proceed with the migrate. ###### 1. Make Migration files ```bash $ migrate create -ext sql -dir ./database/migrations -seq create_initial_table ``` ```console sqlc/database/migrations/000001_create_initial_table.up.sql sqlc/database/migrations/000001_create_initial_table.up.sql ``` ###### 2. Migrate ```bash $ migrate -path database/migrations -database "postgresql://user:password@localhost:5432/fiber_demo?sslmode=disable" -verbose up ``` ```console 2023/09/28 20:00:00 Start buffering 1/u create_initial_table 2023/09/28 20:00:00 Read and execute 1/u create_initial_table 2023/09/28 20:00:00 Finished 1/u create_initial_table (read 24.693541ms, ran 68.30925ms) 2023/09/28 20:00:00 Finished after 100.661625ms 2023/09/28 20:00:00 Closing source and database ``` ###### 3. Rollback Migrate ```bash $ migrate -path database/migrations -database "postgresql://user:password@localhost:5432/fiber_demo?sslmode=disable" -verbose down ``` ```console 2023/09/28 20:00:00 Are you sure you want to apply all down migrations? [y/N] y 2023/09/28 20:00:00 Applying all down migrations 2023/09/28 20:00:00 Start buffering 1/d create_initial_table 2023/09/28 20:00:00 Read and execute 1/d create_initial_table 2023/09/28 20:00:00 Finished 1/d create_initial_table (read 39.681125ms, ran 66.220125ms) 2023/09/28 20:00:00 Finished after 1.83152475s ``` #### 4. Use sqlc ###### 1. Install ```bash # Go 1.25 and above: $ go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest # Legacy Go versions (<1.25): go get github.com/sqlc-dev/sqlc/cmd/sqlc ``` ###### 2. Create a configuration file ###### Example ###### sqlc.yaml ```yaml version: "2" sql: - engine: "postgresql" queries: "database/query" schema: "database/migrations" gen: go: package: "sqlc" out: "database/sqlc" ``` ###### author.sql ```sql -- name: GetAuthors :many SELECT * FROM author; -- name: GetAuthor :one SELECT * FROM author WHERE id = $1; -- name: NewAuthor :one INSERT INTO author (email, name) VALUES ($1, $2) RETURNING *; -- name: UpdateAuthor :one UPDATE author SET email = $1, name = $2 WHERE id = $3 RETURNING *; -- name: DeleteAuthor :exec DELETE FROM author WHERE id = $1; ``` ###### post.sql ```sql -- name: GetPosts :many SELECT * FROM post; -- name: GetPost :one SELECT * FROM post WHERE id = $1; -- name: NewPost :one INSERT INTO post (title, content, author) VALUES ($1, $2, $3) RETURNING *; -- name: UpdatePost :one UPDATE post SET title = $1, content = $2, author = $3 WHERE id = $4 RETURNING *; -- name: DeletePost :exec DELETE FROM post WHERE id = $1; ``` ###### 3. Generate ```bash $ sqlc generate ``` ```text sqlc/ ├── author.sql.go ├── db.go ├── models.go ├── post.sql.go ``` #### 5. Reference [sqlc document](https://docs.sqlc.dev/en/stable/) ## API Endpoints ### Authors | Method | Path | Description | |--------|------|-------------| | GET | `/authors` | List all authors | | GET | `/authors/:id` | Get an author by ID | | POST | `/authors` | Create a new author | | PUT | `/authors/:id` | Update an author | | DELETE | `/authors/:id` | Delete an author | ### Posts | Method | Path | Description | |--------|------|-------------| | GET | `/posts` | List all posts | | GET | `/posts/:id` | Get a post by ID | | POST | `/posts` | Create a new post | | PUT | `/posts/:id` | Update a post | | DELETE | `/posts/:id` | Delete a post | --- ## Server-Sent Events # Server-Sent Events with Fiber [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/sse) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/sse) This example demonstrates how to implement Server-Sent Events (SSE) in a Fiber application. ## Description Server-Sent Events (SSE) allow servers to push updates to the client over a single HTTP connection. This is useful for real-time applications where the server needs to continuously send data to the client, such as live feeds, notifications, or real-time charts. ## Prerequisites - Go 1.25 or higher - Go modules ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/sse ``` 2. Install dependencies: ```sh go mod tidy ``` ## Running the Application 1. Run the application: ```sh go run main.go ``` 2. The server will start on `http://localhost:3000`. ## Endpoints - **GET /**: Index page - **GET /sse**: SSE route - **PUT /publish**: Send messages via SSE ## Example Usage By default, the example will run on port `3000`; this can be changed by modifying the `appPort` constant in `main.go`. The index page connects to the SSE endpoint using a relative URL (`/sse`), so it works regardless of the host or port. 1. Open your browser and navigate to `http://localhost:3000`. 2. The client will automatically connect to the SSE endpoint and start receiving updates from the server. 3. The `/sse` endpoint will publish the current time to the client every two seconds ### Custom Messages To send a custom message, send a `PUT` request to the `/publish` endpoint in the following JSON format ```json { "message": "Hello, World!" } ``` Messages sent to the `/publish` endpoint will be added to a queue that is read from in FIFO order. You can test this by using curl in an iterator If you are using the Bash or Zsh shell: ```sh for i in {1..10}; do curl -X PUT -H 'Content-type: application/json' --data "{\"message\":\"SSE TEST $i\"}" http://localhost:3000/publish done ``` If you are using fish: ```sh for i in (seq 1 10) curl -X PUT -H 'Content-type: application/json' --data "{\"message\":\"SSE TEST $i\"}" http://localhost:3000/publish end ``` Once published, your added messages will begin appearing in the output at `http://localhost:3000`. Once the queue is empty and no user-published messages are left, `/sse` will return to it's standard behavior of displaying the current time. ## Code Overview ### `main.go` The main Go file sets up the Fiber application and handles the SSE connections. It includes the necessary configuration to send events to the client. ## Additional Information Server-Sent Events (SSE) is a standard allowing servers to push data to web clients over HTTP. Unlike WebSockets, which require a full-duplex connection, SSE uses a unidirectional connection from the server to the client. This makes SSE simpler to implement and more efficient for scenarios where only the server needs to send updates. For more information on SSE, you can refer to the following resources: - [Server-Sent Events on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) - [Server-Sent Events on Wikipedia](https://en.wikipedia.org/wiki/Server-sent_events) --- ## Stream Request Body [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/stream-request-body) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/stream-request-body) This project demonstrates how to handle streaming request bodies in a Go application using the Fiber framework. ## Prerequisites Ensure you have the following installed: - Golang - [Fiber](https://github.com/gofiber/fiber) package ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/stream-request-body ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh go run main.go ``` ## Example Here is an example of how to handle a streaming request body in Go using Fiber: ```go package main import ( "io" "log" "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() // Enable request body streaming. app.Server().StreamRequestBody = true // You can test the route by using cURL: // curl -X POST --data-binary @/path/to/large/file localhost:3000 app.Post("/", func(c fiber.Ctx) error { reader := c.RequestCtx().RequestBodyStream() if reader == nil { return c.SendStatus(fiber.StatusOK) } // Read 1MiB at a time buffer := make([]byte, 0, 1024*1024) for { length, err := io.ReadFull(reader, buffer[:cap(buffer)]) // Cap the buffer based on the actual length read buffer = buffer[:length] if length > 0 { // Process the chunk - e.g., write to file, parse data, etc. log.Printf("Read %d bytes", length) } if err != nil { // EOF or ErrUnexpectedEOF means all data has been read. // ErrUnexpectedEOF means the last chunk was smaller than the // buffer, which is normal for the final (or only) chunk. if err == io.EOF || err == io.ErrUnexpectedEOF { break } return err } } return c.SendStatus(fiber.StatusOK) }) log.Fatal(app.Listen(":3000")) } ``` ### curl Example ```sh curl -X POST --data-binary @/path/to/large/file http://localhost:3000 ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Go io Package Documentation](https://pkg.go.dev/io) --- ## Svelte Netlify # Svelte + Fiber on Netlify [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/svelte-netlify) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/svelte-netlify) [![Netlify Status](https://api.netlify.com/api/v1/badges/143c3c42-60f7-427a-b3fd-8ca3947a2d40/deploy-status)](https://app.netlify.com/sites/gofiber-svelte/deploys) A Go Fiber API deployed as a Netlify Function, with a Svelte frontend. The API provides IP geolocation via [ip-api.com](http://ip-api.com). **Demo:** https://gofiber-svelte.netlify.app/ ## Prerequisites - **Go** 1.21+ - **Node.js** 18+ (for the Svelte frontend) - **Netlify CLI** — `npm install -g netlify-cli` ## Local Development 1. Install frontend dependencies and build the Svelte app: ```bash npm install npm run build ``` 2. Build the Go function: ```bash ./build.sh ``` 3. Start the local dev server: ```bash netlify dev ``` The app will be available at `http://localhost:8888`. ## Deploy to Netlify ### Via Netlify CLI ```bash netlify deploy --prod ``` ### Via Git Connect your repository in the Netlify dashboard. The `netlify.toml` configures the build automatically. ## How It Works - `./build.sh` compiles the Go binary to `cmd/gateway/gateway` and places it in the `functions/` directory. - Netlify serves the binary as a [Netlify Function](https://functions.netlify.com/). - Static files under `public/` are served directly (entry point: `index.html`). - API calls to `/api/*` are redirected server-side to `/.netlify/functions/gateway/:splat` (status 200). ## Project Structure ``` . ├── cmd/gateway/ # Lambda entry point (main.go) ├── handler/ # Fiber route handlers ├── public/ # Compiled Svelte frontend ├── build.sh # Build script for the Go function └── netlify.toml # Netlify build configuration ``` ## Notes - Netlify Functions are limited to 125,000 requests/month on the free tier (~2.89 req/min). Response caching with a 10-minute TTL is applied to the geolocation endpoint to stay within limits. --- ## Sveltekit Embed # Fiber Sveltekit Embed App [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/sveltekit-embed) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/sveltekit-embed) ![image](https://github.com/gofiber/recipes/assets/40540244/2aa084b8-9bbc-46f3-9759-930857429f05) This application is a full-stack project built using Sveltekit, Tailwind CSS, Fiber. It showcases the construction of a monolithic architecture for a full-stack application. ## Run the Project To run the project, follow these steps: 1. Execute the following command to run all the necessary commands for building and running the application: ```bash make all ``` 2. Once the build process is complete, you can start the application by running: ```bash ./app ``` ## Available Commands The following commands are available to manage the project: | Command | Description | | --- | --- | | `info` | Info command. Displays the available commands and the purpose of the application. | | `go-build` | Builds the Golang project and creates an `app` file. | | `svelte-build` | Builds the SvelteKit project. It first installs the dependencies and then performs the project build. | | `all` | Runs both `svelte-build` and `go-build` commands sequentially. | ## Usage To use this application, run the following command: ```bash make ``` API Routes ---------- The Go Fiber application provides the following API routes: | Route | Description | | --- | --- | | `/*` | Serves static files from the specified directory (`template.Dist()`). If a file is not found, it serves `index.html`. | Go Dependencies --------------- - **Go Modules:** Go's built-in package manager used to manage dependencies for Go projects. - **Fiber:** A fast and minimalist web framework for Golang. Npm Dependencies ---------------- - **SvelteKit:** A JavaScript framework used to build modern web applications. - **Tailwind CSS:** A fast and customizable CSS styling library. Can be used in SvelteKit projects. - **Skeleton UI:** This is a fully featured UI Toolkit for building reactive interfaces quickly using Svelte and Tailwind. ---------------- Author: [@ugurkorkmaz](https://github.com/ugurkorkmaz) --- ## create-svelte Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). ## Creating a project If you're seeing this, you've probably already done this step. Congrats! ```bash # create a new project in the current directory npm create svelte@latest # create a new project in my-app npm create svelte@latest my-app ``` ## Developing Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: ```bash npm run dev # or start the server and open the app in a new browser tab npm run dev -- --open ``` ## Building To create a production version of your app: ```bash npm run build ``` You can preview the production build with `npm run preview`. > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. --- ## Swagger # Swagger API Documentation [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/swagger) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/swagger) This project demonstrates how to integrate Swagger for API documentation in a [Fiber](https://github.com/gofiber/fiber) application using [`gofiber/contrib/swaggerui`](https://github.com/gofiber/contrib/tree/main/swaggerui) and [`swaggo/swag`](https://github.com/swaggo/swag). ## Prerequisites - Go 1.21+ - [Swag CLI](https://github.com/swaggo/swag) for generating Swagger docs - PostgreSQL (connection configured via environment variables) ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/swagger ``` 2. Install the Swag CLI: ```sh go install github.com/swaggo/swag/cmd/swag@latest ``` 3. Configure the database connection via environment variables: ```sh export DB_USER=postgres export DB_PASSWORD=secret export DB_HOST=localhost export DB_NAME=books export DB_PORT=5432 ``` ## Generating Swagger Docs Generate (or regenerate) the Swagger documentation from the annotations in your source code: ```sh swag init ``` This writes `docs/docs.go`, `docs/swagger.json`, and `docs/swagger.yaml`. ## Running the Application ```sh go run main.go ``` The application starts on port **3000**. ## Accessing the Swagger UI Open your browser and navigate to: ``` http://localhost:3000/docs/docs ``` The Swagger UI is served by [`gofiber/contrib/v3/swaggerui`](https://github.com/gofiber/contrib/tree/main/swaggerui) and reads the spec from `./docs/swagger.json`. ## API Endpoints All routes are prefixed with `/api/v1`. | Method | Path | Description | |--------|------|-------------| | GET | `/api/v1/books` | List all books | | GET | `/api/v1/books/:id` | Get a book by ID | | POST | `/api/v1/books` | Register a new book | | DELETE | `/api/v1/books/:id` | Delete a book by ID | ## Example Annotate your Fiber handler functions with Swag comments to generate docs: ```go // GetBookByID returns a book by ID // @Summary Get book by ID // @Description Get a single book by its ID // @Tags books // @Accept json // @Produce json // @Param id path int true "Book ID" // @Success 200 {object} ResponseHTTP{data=models.Book} // @Failure 404 {object} ResponseHTTP{} // @Router /v1/books/{id} [get] func GetBookByID(c fiber.Ctx) error { // Your code here } ``` ## curl Examples ```sh # List all books curl http://localhost:3000/api/v1/books # Get book by ID curl http://localhost:3000/api/v1/books/1 # Create a book curl -X POST http://localhost:3000/api/v1/books \ -H "Content-Type: application/json" \ -d '{"title":"The Go Programming Language","author":"Donovan & Kernighan"}' # Delete a book curl -X DELETE http://localhost:3000/api/v1/books/1 ``` ## References - [Fiber](https://github.com/gofiber/fiber) - [gofiber/contrib/swaggerui](https://github.com/gofiber/contrib/tree/main/swaggerui) - [Swag Documentation](https://github.com/swaggo/swag) --- ## Tableflip Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/tableflip) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/tableflip) This example demonstrates how to use [tableflip](https://github.com/cloudflare/tableflip) for graceful upgrades in a Go application. ## What is Tableflip? Tableflip is a library that allows you to update the running code and/or configuration of a network service without disrupting existing connections. It achieves this by starting a new process, transferring clients to it, and then exiting the old process. ### Goals of Tableflip - No old code keeps running after a successful upgrade. - The new process has a grace period for initialization. - Crashing during initialization is acceptable. - Only a single upgrade is ever run in parallel. - Tableflip works on Linux and macOS. ## Steps 1. **Build v0.0.1 Demo:** ```bash go build -o demo main.go ``` 2. **Run the Demo and Create a GET Request to `127.0.0.1:8080/version`:** ```bash [PID: 123] v0.0.1 ``` 3. **Prepare a New Version:** - Change the `main.go` to update the version to "v0.0.2". - Rebuild the demo: ```bash go build -o demo main.go ``` 4. **Kill the Old Process:** ```bash kill -s HUP 123 ``` 5. **Create the Request to the Version API Again:** ```bash [PID: 123] v0.0.2 ``` The client is completely immune to server upgrades and reboots, and our application updates gracefully! --- ## Template # Template Project [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/template) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/template) This project demonstrates how to set up a Go application with template rendering using the Django template engine. ## Prerequisites Ensure you have the following installed: - Golang ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/template ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the Fiber application: ```sh go run main.go ``` ## Example Here is an example of how to set up a basic route with template rendering in Go: ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/django/v4" ) func main() { // Initialize the template engine engine := django.New("./views", ".html") // Create a new Fiber instance with the template engine app := fiber.New(fiber.Config{ Views: engine, }) // Define a route app.Get("/", func(c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) log.Fatal(app.Listen(":3000")) } ``` ## References - [Fiber Documentation](https://docs.gofiber.io) - [Fiber Template Documentation](https://github.com/gofiber/template) --- ## Template Asset Bundling [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/template-asset-bundling) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/template-asset-bundling) This example demonstrates how to integrate asset bundling into a Go web application using [Fiber](https://github.com/gofiber/fiber), [gofiber/template](https://github.com/gofiber/template) for HTML template rendering, [Tailwind CSS](https://tailwindcss.com) for utility-first styling, and [Parcel](https://parceljs.org) as a zero-configuration asset bundler. Parcel processes and hashes the CSS assets, which are then served as static files by Fiber. ## Prerequisites Ensure you have the following installed: - [Go](https://golang.org/dl/) 1.21+ - [Node.js](https://nodejs.org/) 18+ and npm ## Project Structure ``` template-asset-bundling/ ├── app.go # Fiber application entry point ├── handlers/ │ └── handlers.go # Route handlers (Home, About, NotFound) ├── views/ │ ├── layouts/ │ │ └── main.html # Base layout template │ ├── partials/ # Reusable template partials │ ├── index.html # Home page template │ ├── about.html # About page template │ └── 404.html # Not found template ├── assets/ │ └── app.css # Tailwind CSS source (input) ├── public/ │ └── assets/ # Compiled assets output (git-ignored) ├── package.json # Node dependencies and npm scripts ├── tailwind.config.js # Tailwind CSS configuration └── .postcssrc # PostCSS configuration for Parcel ``` ## Setup 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/template-asset-bundling ``` 2. Install Node.js dependencies: ```sh npm install ``` 3. Install Go dependencies: ```sh go mod download ``` ## Running ### Development Run the asset watcher and the Go server in separate terminals: ```sh # Terminal 1 — watch and rebuild assets on change npm run dev # Terminal 2 — start the Fiber server (template hot-reload enabled) go run app.go ``` Open [http://localhost:3000](http://localhost:3000) in your browser. ### Production Build optimized assets first, then run the server with `APP_ENV=production` to disable template hot-reloading: ```sh npm run build APP_ENV=production go run app.go ``` ## How It Works - `npm run dev` runs Parcel in watch mode, compiling `assets/app.css` (Tailwind source) into hashed output files under `public/assets/`. - The `getCssAsset` template function walks `public/assets/` at render time to find the correct hashed filename and injects the `` tag automatically. - In development (`APP_ENV` is not `production`), `engine.Reload(true)` re-parses templates on every request so changes are reflected without restarting the server. --- ## Todo App + Auth + GORM # Todo App with Auth using GORM [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/todo-app-with-auth-gorm) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/todo-app-with-auth-gorm) This project demonstrates a Todo application with authentication using GORM. ## Prerequisites Ensure you have the following installed and available in your `GOPATH`: - Golang - [Air](https://github.com/air-verse/air) for hot reloading - [Godotenv](https://github.com/joho/godotenv) for loading `.env` file ## Installation 1. Clone the repository: ```sh git clone https://github.com/gofiber/recipes.git cd recipes/todo-app-with-auth-gorm ``` 2. Install dependencies: ```sh go get ``` ## Running the Application 1. Start the application: ```sh air ``` ## Environment Variables Create a `.env` file in the root directory and add the following variables: ```shell # PORT returns the server listening port # default: 5000 PORT= # DB returns the name of the sqlite database # default: gotodo.db DB= # TOKENKEY returns the jwt token secret TOKENKEY= # TOKENEXP returns the jwt token expiration duration. # Should be time.ParseDuration string. Source: https://golang.org/pkg/time/#ParseDuration # default: 10h TOKENEXP= ``` --- ## Unit Testing # Unit Testing Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/unit-test) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/unit-test) This example demonstrates how to write unit tests for a Go Fiber application using the `stretchr/testify` package. ## Description This project provides a basic setup for unit testing in a Go Fiber application. It includes examples of how to structure tests, write test cases, and use the `stretchr/testify` package for assertions. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Project Structure - `main.go`: The main application entry point. - `main_test.go`: The test file containing unit tests. - `go.mod`: The Go module file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/unit-test ``` 2. Install the dependencies: ```bash go mod download ``` ## Running the Tests To run the tests, use the following command: ```bash go test ./... ``` ## Example Usage The `main.go` file sets up a simple Fiber application with a single route. The `main_test.go` file contains unit tests for this application. ### `main.go` This file sets up a basic Fiber application with a single route that returns "OK". ### `main_test.go` This file contains unit tests for the Fiber application. It uses the `stretchr/testify` package for assertions. ```go package main import ( "io" "net/http" "testing" "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" ) func TestIndexRoute(t *testing.T) { tests := []struct { description string route string expectedError bool expectedCode int expectedBody string }{ { description: "index route", route: "/", expectedError: false, expectedCode: 200, expectedBody: "OK", }, { description: "non existing route", route: "/i-dont-exist", expectedError: false, expectedCode: 404, expectedBody: "Cannot GET /i-dont-exist", }, } app := Setup() for _, test := range tests { req, err := http.NewRequest("GET", test.route, nil) assert.Nilf(t, err, test.description) res, err := app.Test(req, fiber.TestConfig{Timeout: 0, FailOnTimeout: false}) assert.Equalf(t, test.expectedError, err != nil, test.description) if test.expectedError { continue } defer res.Body.Close() assert.Equalf(t, test.expectedCode, res.StatusCode, test.description) body, err := io.ReadAll(res.Body) assert.Nilf(t, err, test.description) assert.Equalf(t, test.expectedBody, string(body), test.description) } } ``` ## Unit Testing in General Unit testing is a software testing method where individual units or components of a software are tested in isolation from the rest of the application. The purpose of unit testing is to validate that each unit of the software performs as expected. Unit tests are typically automated and written by developers as part of the development process. ### Benefits of Unit Testing - **Early Bug Detection**: Unit tests help in identifying bugs early in the development cycle. - **Documentation**: Unit tests can serve as documentation for the code. - **Refactoring Support**: Unit tests provide a safety net when refactoring code. - **Design**: Writing unit tests can lead to better software design. ## Unit Testing in Fiber Fiber is an Express-inspired web framework written in Go. Unit testing in Fiber involves testing the individual routes and handlers to ensure they behave as expected. The `stretchr/testify` package is commonly used for writing assertions in Go tests. ### Writing Unit Tests in Fiber 1. **Setup the Application**: Create a function to setup the Fiber application. This function can be reused in the tests. 2. **Define Test Cases**: Create a structure to define the input and expected output for each test case. 3. **Perform Requests**: Use the `app.Test` method to perform HTTP requests and capture the response. 4. **Assertions**: Use the `stretchr/testify` package to write assertions and verify the response. ### The `app.Test` Method The `app.Test` method in Fiber is used to simulate HTTP requests to the Fiber application and test the responses. This is particularly useful for unit tests as it allows testing the routes and handlers of the application without starting a real server. #### Usage of the `app.Test` Method The `app.Test` method takes two parameters: 1. **req**: An `*http.Request` object representing the HTTP request to be tested. 2. **config**: A `fiber.TestConfig` struct for configuring the test (e.g., `Timeout` and `FailOnTimeout`). The method returns an `*http.Response` and an `error`. The `*http.Response` contains the application's response to the simulated request, and the `error` indicates if any error occurred during the request processing. #### Example Here is an example of how the `app.Test` method is used in a unit test: ```go package main import ( "io" "net/http" "testing" "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" ) func TestIndexRoute(t *testing.T) { // Setup the app as it is done in the main function app := Setup() // Create a new HTTP request req, err := http.NewRequest("GET", "/", nil) assert.Nil(t, err) // Perform the request using app.Test res, err := app.Test(req, fiber.TestConfig{Timeout: 0, FailOnTimeout: false}) defer res.Body.Close() // Verify that no error occurred assert.Nil(t, err) // Verify the status code assert.Equal(t, 200, res.StatusCode) // Read the response body body, err := io.ReadAll(res.Body) assert.Nil(t, err) // Verify the response body assert.Equal(t, "OK", string(body)) } ``` In this example, a GET request is sent to the root route (`"/"`) of the application. The response is verified to ensure that the status code is `200` and the response text is `"OK"`. ## Conclusion This example provides a basic setup for unit testing in a Go Fiber application. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [Testify Documentation](https://github.com/stretchr/testify) - [Go Testing](https://golang.org/pkg/testing/) --- ## File Upload # File Upload Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/upload-file) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/upload-file) This example demonstrates how to handle file uploads using Go Fiber. ## Description This project provides a basic setup for handling file uploads in a Go Fiber application. It includes examples for uploading single and multiple files, as well as saving files to different directories. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Project Structure - `single/main.go`: Example for uploading a single file to the root directory. - `single_relative_path/main.go`: Example for uploading a single file to a relative path. - `multiple/main.go`: Example for uploading multiple files. - `go.mod`: The Go module file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/upload-file ``` 2. Install the dependencies: ```bash go mod download ``` ## Running the Examples ### Single File Upload 1. Navigate to the `single` directory: ```bash cd single ``` 2. Run the application: ```bash go run main.go ``` 3. Use a tool like `curl` or Postman to upload a file: ```bash curl -F "document=@/path/to/your/file" http://localhost:3000/ ``` ### Single File Upload with Relative Path 1. Navigate to the `single_relative_path` directory: ```bash cd single_relative_path ``` 2. Run the application: ```bash go run main.go ``` 3. Use a tool like `curl` or Postman to upload a file: ```bash curl -F "document=@/path/to/your/file" http://localhost:3000/ ``` ### Multiple File Upload 1. Navigate to the `multiple` directory: ```bash cd multiple ``` 2. Run the application: ```bash go run main.go ``` 3. Use a tool like `curl` or Postman to upload multiple files: ```bash curl -F "documents=@/path/to/your/file1" -F "documents=@/path/to/your/file2" http://localhost:3000/ ``` ## Security - **Filename sanitization**: All handlers use `filepath.Base(file.Filename)` to strip any directory components from uploaded filenames, preventing path traversal attacks (e.g., `../../etc/passwd`). - **Body size limit**: The app is configured with a 10 MB body limit (`BodyLimit: 10 * 1024 * 1024`) to prevent denial-of-service via large uploads. ## Code Overview ### `single/main.go` Handles uploading a single file to the root directory. ### `single_relative_path/main.go` Handles uploading a single file to a relative path (`./uploads/`) or a temp uploads directory (`./uploads_relative/`). ### `multiple/main.go` Handles uploading multiple files. ## Conclusion This example provides a basic setup for handling file uploads in a Go Fiber application. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) --- ## URL Shortener # URL Shortener API [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/url-shortener-api) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/url-shortener-api) This project provides a URL shortening service built with [Fiber](https://gofiber.io) and Redis. ## Tech Stack - Go + [Fiber v3](https://gofiber.io) - Redis (via [go-redis/v8](https://github.com/go-redis/redis)) ## Environment Variables Copy `api/.env.example` to `api/.env` and adjust the values: | Variable | Default | Description | |-------------|------------------|------------------------------------------------| | `DB_ADDR` | `localhost:6379` | Redis address (`host:port`) | | `DB_PASS` | _(empty)_ | Redis password (leave empty if none) | | `APP_PORT` | `:3000` | Port the API listens on (include leading colon)| | `DOMAIN` | `localhost:3000` | Public domain used to build the short URL | | `API_QUOTA` | `10` | Max API calls per IP per 30-minute window | ## Quick Start ### Redis only (local dev) Start Redis with Docker Compose: ```sh docker compose up db -d ``` Copy and edit the env file, then run the API: ```sh cp api/.env.example api/.env cd api && go run . ``` ### Full stack (API + Redis) ```sh docker compose up -d ``` ## API Documentation **Endpoint:** `POST http://localhost:3000/api/v1` ### Request body | Field | Type | Required | Description | |---------|--------|----------|------------------------------------------| | `url` | string | yes | The original URL to shorten | | `short` | string | no | Custom alias (auto-generated if omitted) | | `expiry`| int | no | Expiry in hours (default: 24) | ### Response body | Field | Type | Description | |--------------------|--------|------------------------------------------| | `url` | string | Original URL | | `short` | string | Full short URL including domain | | `expiry` | int | Expiry in hours | | `rate_limit` | int | Remaining API calls in current window | | `rate_limit_reset` | int | Minutes until rate limit window resets | > Rate limit: 10 calls per IP every 30 minutes (configurable via `API_QUOTA` in `.env`). ### curl Examples **Shorten a URL (auto-generated alias):** ```sh curl -X POST http://localhost:3000/api/v1 \ -H "Content-Type: application/json" \ -d '{"url": "https://gofiber.io", "expiry": 24}' ``` **Shorten a URL with a custom alias:** ```sh curl -X POST http://localhost:3000/api/v1 \ -H "Content-Type: application/json" \ -d '{"url": "https://gofiber.io", "short": "fiber", "expiry": 48}' ``` **Resolve a short URL (browser or curl):** ```sh curl -L http://localhost:3000/fiber ``` ## Setup --- ## Validation # Validation with [Fiber](https://gofiber.io) [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/validation) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/validation) This example demonstrates how to use [go-playground/validator](https://github.com/go-playground/validator) for input validation in a Go Fiber application. ## Description This project provides a basic setup for validating request data in a Go Fiber application using the `go-playground/validator` package. It includes the necessary configuration and code to perform validation on incoming requests. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Project Structure - `main.go`: The main application entry point. - `config/env.go`: Configuration file for environment variables. - `go.mod`: The Go module file. - `.env`: Environment variables file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/validation ``` 2. Install the dependencies: ```bash go mod download ``` 3. Create a `.env` file in the root directory with the following content: ```dotenv PORT=":8080" ``` 4. Run the application: ```bash go run main.go ``` The application should now be running on `http://localhost:8080`. ## Example Usage 1. Send a POST request to `http://localhost:8080/validate` with a JSON payload: ```json { "name": "John Doe", "email": "john.doe@example.com", "age": 30 } ``` 2. The server will validate the request data and respond with a success message if the data is valid, or an error message if the data is invalid. ## Code Overview ### `main.go` The main Go file sets up the Fiber application, handles HTTP requests, and performs validation using the `go-playground/validator` package. ### `config/env.go` The configuration file for loading environment variables. ```go package config import "os" // Config func to get env value func Config(key string) string { return os.Getenv(key) } ``` ## Conclusion This example provides a basic setup for validating request data in a Go Fiber application using the `go-playground/validator` package. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [Validator Documentation](https://github.com/go-playground/validator) --- ## Vercel # Vercel Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/vercel) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/vercel) This example demonstrates how to deploy a Go Fiber application to Vercel. ## Description This project provides a starting point for deploying a Go Fiber application to Vercel. It includes the necessary configuration files and code to run a serverless application on Vercel. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) - [Vercel CLI](https://vercel.com/download) - A folder named `public` with one or more static files to be served > [!CAUTION] > If you don't have a `public` folder, Vercel will serve all files from your project root, which can expose sensitive files like your source code. ## Project Structure - `api/index.go`: The main entry point for the serverless function. - `vercel.json`: Configuration file for Vercel. - `go.mod`: The Go module file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/vercel ``` 2. Install the dependencies: ```bash go mod download ``` ## Configuration Ensure the `vercel.json` file is present in the root directory to handle routing properly. This file rewrites all requests to the `api/index.go` handler. ```json { "rewrites": [ { "source": "(.*)", "destination": "api/index.go" } ] } ``` ## Deploy 1. Install the Vercel CLI: ```bash npm install -g vercel ``` 2. Log in to Vercel: ```bash vercel login ``` 3. Deploy the application: ```bash vercel ``` Follow the prompts to complete the deployment. Your application will be deployed to Vercel and a URL will be provided. ## Example Usage 1. Open your browser and navigate to the provided Vercel URL. 2. You should see the JSON response with the URI and path. ## Code Overview ### `api/index.go` The main Go file sets up the Fiber application, handles HTTP requests, and manages the routing. ```go package handler import ( "github.com/gofiber/fiber/v3/middleware/adaptor" "github.com/gofiber/fiber/v3" "net/http" ) // Handler is the main entry point of the application. Think of it like the main() method func Handler(w http.ResponseWriter, r *http.Request) { // This is needed to set the proper request path in `fiber.Ctx` r.RequestURI = r.URL.String() handler().ServeHTTP(w, r) } // building the fiber application func handler() http.HandlerFunc { app := fiber.New() app.Get("/v1", func(ctx fiber.Ctx) error { return ctx.JSON(fiber.Map{ "version": "v1", }) }) app.Get("/v2", func(ctx fiber.Ctx) error { return ctx.JSON(fiber.Map{ "version": "v2", }) }) app.Get("/", func(ctx fiber.Ctx) error { return ctx.JSON(fiber.Map{ "uri": ctx.Request().URI().String(), "path": ctx.Path(), }) }) return adaptor.FiberApp(app) } ``` ## Conclusion This example provides a basic setup for deploying a Go Fiber application to Vercel. It can be extended and customized further to fit the needs of more complex applications. ## References - [Vercel Documentation](https://vercel.com/docs) - [Fiber Documentation](https://docs.gofiber.io) --- ## WebSocket(Websocket) # WebSocket Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/websocket) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/websocket) This example demonstrates a simple WebSocket application using Go Fiber. ## Description This project provides a basic setup for a WebSocket server using Go Fiber. It includes the necessary configuration and code to run a real-time WebSocket server. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Project Structure - `main.go`: The main application entry point. - `go.mod`: The Go module file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/websocket ``` 2. Install the dependencies: ```bash go mod download ``` 3. Run the application: ```bash go run main.go ``` The application should now be running on `http://localhost:3000`. ## WebSocket Endpoint - **GET /ws**: WebSocket endpoint for the application. ## Example Usage 1. Connect to the WebSocket server at `ws://localhost:3000/ws`. 2. Send a message to the server. 3. The server will echo the message back to the client. ## Code Overview ### `main.go` The main Go file sets up the Fiber application, handles WebSocket connections, and manages the WebSocket communication. ```go package main import ( "fmt" "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/contrib/v3/websocket" ) func main() { app := fiber.New() // Optional middleware app.Use("/ws", func(c fiber.Ctx) error { if c.Get("host") == "localhost:3000" { c.Locals("Host", "Localhost:3000") return c.Next() } return c.Status(fiber.StatusForbidden).SendString("Request origin not allowed") }) // Upgraded websocket request app.Get("/ws", websocket.New(func(c *websocket.Conn) { fmt.Println(c.Locals("Host")) // "Localhost:3000" for { mt, msg, err := c.ReadMessage() if err != nil { log.Println("read:", err) break } log.Printf("recv: %s", msg) err = c.WriteMessage(mt, msg) if err != nil { log.Println("write:", err) break } } })) // ws://localhost:3000/ws log.Fatal(app.Listen(":3000")) } ``` ## Conclusion This example provides a basic setup for a WebSocket server using Go Fiber. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [WebSocket Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) --- ## WebSocket Chat # WebSocket Chat Example [![Github](https://img.shields.io/static/v1?label=&message=Github&color=2ea44f&style=for-the-badge&logo=github)](https://github.com/gofiber/recipes/tree/master/websocket-chat) [![StackBlitz](https://img.shields.io/static/v1?label=&message=StackBlitz&color=2ea44f&style=for-the-badge&logo=StackBlitz)](https://stackblitz.com/github/gofiber/recipes/tree/master/websocket-chat) This example demonstrates a simple chat application using Go Fiber and WebSockets. ## Description This project provides a basic setup for a WebSocket-based chat application using Go Fiber. It includes the necessary configuration and code to run a real-time chat server. ## Requirements - [Go](https://golang.org/dl/) 1.18 or higher - [Git](https://git-scm.com/downloads) ## Project Structure - `main.go`: The main application entry point. - `home.html`: The HTML file for the chat client. - `go.mod`: The Go module file. ## Setup 1. Clone the repository: ```bash git clone https://github.com/gofiber/recipes.git cd recipes/websocket-chat ``` 2. Install the dependencies: ```bash go mod download ``` 3. Run the application: ```bash go run main.go ``` The application should now be running on `http://localhost:8080`. ## WebSocket Endpoints - **GET /ws**: WebSocket endpoint for the chat application. ## Example Usage 1. Open your browser and navigate to `http://localhost:8080`. 2. Enter a message in the input field and click "Send". 3. The message should appear in the chat log. ## Code Overview ### `main.go` The main Go file sets up the Fiber application, handles WebSocket connections, and manages the chat hub. - Defines a `client` struct with a mutex and a closing flag to guard concurrent writes per connection. - Declares global channels (`register`, `broadcast`, `unregister`) and a shared `clients` map. - `runHub()` runs in a goroutine and serialises map mutations: it adds connections on `register`, fans out messages to all clients in parallel goroutines on `broadcast`, and removes connections on `unregister`. - `main()` parses the `--addr` flag, serves `home.html` as a static file, upgrades `/ws` requests to WebSocket, and starts the hub before listening. ### `home.html` The HTML file provides a simple user interface for the chat application, including a message log and input field. ## Conclusion This example provides a basic setup for a WebSocket-based chat application using Go Fiber. It can be extended and customized further to fit the needs of more complex applications. ## References - [Fiber Documentation](https://docs.gofiber.io) - [WebSocket Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) --- ## 👋 Welcome(Storage) Premade storage drivers that implement the [`Storage`](https://github.com/gofiber/storage/blob/main/storage.go) interface, designed to be used with various [Fiber middlewares](https://github.com/gofiber/fiber/tree/master/middleware). **Note:** All storages are tested with the latest two [Go version](https://go.dev/doc/devel/release#policy). Older Go versions may also work, but are not guaranteed to be supported. ```go // Storage interface for communicating with different database/key-value // providers. Visit https://github.com/gofiber/storage for more info. type Storage interface { // GetWithContext gets the value for the given key with a context. // `nil, nil` is returned when the key does not exist GetWithContext(ctx context.Context, key string) ([]byte, error) // Get gets the value for the given key. // `nil, nil` is returned when the key does not exist Get(key string) ([]byte, error) // SetWithContext stores the given value for the given key // with an expiration value, 0 means no expiration. SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error // Set stores the given value for the given key along // with an expiration value, 0 means no expiration. // Empty key or value will be ignored without an error. Set(key string, val []byte, exp time.Duration) error // DeleteWithContext deletes the value for the given key with a context. // It returns no error if the storage does not contain the key, DeleteWithContext(ctx context.Context, key string) error // Delete deletes the value for the given key. // It returns no error if the storage does not contain the key, Delete(key string) error // ResetWithContext resets the storage and deletes all keys with a context. ResetWithContext(ctx context.Context) error // Reset resets the storage and delete all keys. Reset() error // Close closes the storage and will stop any running garbage // collectors and open connections. Close() error } ``` ## 📑 Storage Implementations - [Aerospike](./aerospike/README.md) - [ArangoDB](./arangodb/README.md) - [AzureBlob](./azureblob/README.md) - [Badger](./badger/README.md) - [Bbolt](./bbolt) - [Cassandra](./cassandra/README.md) - [ClickHouse](./clickhouse/README.md) - [CloudflareKV](./cloudflarekv/README.md) - [Coherence](./coherence/README.md) - [Couchbase](./couchbase/README.md) - [DynamoDB](./dynamodb/README.md) - [Etcd](./etcd/README.md) - [Firestore](./firestore/README.md) - [LevelDB](./leveldb/README.md) - [Memcache](./memcache/README.md) - [Memory](./memory/README.md) - [Minio](./minio/README.md) - [MockStorage](./mockstorage/README.md) - [MongoDB](./mongodb/README.md) - [MSSQL](./mssql/README.md) - [MySQL](./mysql/README.md) - [NATS](./nats/README.md) - [Neo4j](./neo4j/README.md) - [Pebble](./pebble/README.md) - [Postgres](./postgres/README.md) - [Redis](./redis/README.md) - [Ristetto](./ristretto/README.md) - [Rueidis](./rueidis/README.md) - [S3](./s3/README.md) - [ScyllaDB](./scylladb/README.md) - [SQLite3](./sqlite3/README.md) - [SurrealDB](./surrealdb/README.md) - [Valkey](./valkey/README.md) See the benchmarks under https://gofiber.github.io/storage/benchmarks --- ## Aerospike ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=aerospike*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-aerospike.yml?label=Tests) An Aerospike client driver using `aerospike/aerospike-client-go` and [aerospike/aerospike-client-go](https://github.com/aerospike/aerospike-client-go). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() driver.Client func (s *Storage) GetSchemaInfo() *SchemaInfo ``` **Note:** The context methods are dummy methods and don't have any functionality, as Aerospike does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Aerospike is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the aerospike implementation: ```bash go get github.com/gofiber/storage/aerospike ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/aerospike" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := aerospike.New() // Initialize custom config store := aerospike.New(aerospike.Config{ Hosts: []*aerospike.Host{aerospike.NewHost("localhost", 3000)}, Namespace: "test", // Default namespace SetName: "fiber", Reset: false, Expiration: 1 * time.Hour, SchemaVersion: 1, SchemaDescription: "Default Fiber storage schema", ForceSchemaUpdate: false, }) ``` ### Config ```go type Config struct { // Hosts is a list of Aerospike server hosts Hosts []*aerospike.Host // Namespace is the Aerospike namespace Namespace string // Set is the Aerospike set SetName string // Reset clears any existing keys in existing Set Reset bool // Expiration is the default expiration time of entries Expiration time.Duration // SchemaVersion indicates the schema version to use SchemaVersion int // SchemaDescription provides additional info about the schema SchemaDescription string // ForceSchemaUpdate forces schema update even if version matches ForceSchemaUpdate bool } ``` ### Default Config Used only for optional fields ```go var ConfigDefault = Config{ Hosts: []*aerospike.Host{aerospike.NewHost("localhost", 3000)}, Namespace: "test", // Default namespace SetName: "fiber", Reset: false, Expiration: 1 * time.Hour, SchemaVersion: 1, SchemaDescription: "Default Fiber storage schema", ForceSchemaUpdate: false, } ``` --- ## ArangoDB ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=arangodb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-arangodb.yml?label=Tests) A ArangoDB storage driver using `arangodb/go-driver` and [arangodb/go-driver](https://github.com/arangodb/go-driver). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Delete(key string) error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Reset() error func (s *Storage) Close() error func (s *Storage) Conn() driver.Client ``` ### Installation ArangoDB is tested on the 2 last (1.14/1.15) [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the arangodb implementation: ```bash go get github.com/gofiber/storage/arangodb/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/arangodb/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := arangodb.New() // Initialize custom config store := arangodb.New(arangodb.Config{ Host: "http://127.0.0.1", Port: 8529, Database: "fiber", Collection: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, }) ``` ### Config ```go type Config struct { // Host name where the DB is hosted // // Optional. Default is "http://127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 8529 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Database name // // Optional. Default is "fiber" Database string // Collection name // // Optional. Default is "fiber_storage" Collection string // Reset clears any existing keys in existing collection // // Optional. Default is false Reset bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration } ``` ### Default Config Used only for optional fields ```go var ConfigDefault = Config{ Host: "http://127.0.0.1", Port: 8529, Database: "fiber", Collection: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, } ``` --- ## Azure Blob ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=azureblob*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-azureblob.yml?label=Tests) [Azure Blob storage](https://azure.microsoft.com/en-us/products/storage/blobs/#overview) is Microsoft's object storage solution for the cloud. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *azblob.Client ``` ### Installation Azure blob storage driver is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the azure blob implementation: ```bash go get github.com/gofiber/storage/azureblob/v2 ``` ### Examples Import the storage package. ```go ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := azureblob.New() // Initialize custom config store := azureblob.New(azureblob.Config{ Account: "test", Container: "test", Credentials: Credentials{ Account: "test", Key: "YXp1cml0ZWtleQo=", }, }) ``` ### Config ```go type Config struct { // Storage account name. Account string // Container name. Container string // Storage endpoint. // Optional. Default: "https://STORAGEACCOUNTNAME.blob.core.windows.net" Endpoint string // Request timeout. // Optional. Default is 0 (no timeout) RequestTimeout time.Duration // Reset clears any existing keys in existing container. // Optional. Default is false Reset bool // Credentials overrides AWS access key and AWS secret access key. Not recommended. // Optional. Default is Credentials{} Credentials Credentials // The maximum number of times requests that encounter retryable failures should be attempted. // Optional. Default is 3 MaxAttempts int } ``` ### Default Config ```go var ConfigDefault = Config{ Account: "", Container: "", Endpoint: "", RequestTimeout: 0, Reset: false, MaxAttempts: 3, } ``` --- ## Badger ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=badger*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-badger.yml?label=Tests) A fast key-value DB using [dgraph-io/badger](https://github.com/dgraph-io/badger) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *badger.DB ``` **Note:** The context methods are dummy methods and don't have any functionality, as Badger does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Badger is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the badger implementation: ```bash go get github.com/gofiber/storage/badger/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/badger/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := badger.New() // Initialize custom config store := badger.New(badger.Config{ Database: "./fiber.badger", Reset: false, GCInterval: 10 * time.Second, }) ``` ### Config ```go type Config struct { // Database name // // Optional. Default is "./fiber.badger" Database string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration // BadgerOptions is a way to set options in badger // // Optional. Default is badger.DefaultOptions("./fiber.badger") BadgerOptions badger.Options // Logger is the default logger used by badger // // Optional. Default is nil Logger badger.Logger // UseLogger define if any logger will be used // // Optional. Default is false UseLogger bool } ``` ### Default Config ```go var ConfigDefault = Config{ Database: "./fiber.badger", Reset: false, GCInterval: 10 * time.Second, BadgerOptions: badger.DefaultOptions("./fiber.badger").WithLogger(nil), Logger: nil, UseLogger: false, } ``` --- ## Bbolt ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=bbolt*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-bbolt.yml?label=Tests) A Bbolt storage driver using [etcd-io/bbolt](https://github.com/etcd-io/bbolt). Bolt is a pure Go key/value store inspired by [Howard Chu's](https://twitter.com/hyc_symas) [LMDB project](https://www.symas.com/symas-embedded-database-lmdb). The goal of the project is to provide a simple, fast, and reliable database for projects that don't require a full database server such as Postgres or MySQL. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *bbolt.DB ``` **Note:** The context methods are dummy methods and don't have any functionality, as Bbolt does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Bbolt is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the s3 implementation: ```bash go get github.com/gofiber/storage/bbolt/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/bbolt/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := bbolt.New() // Initialize custom config store := bbolt.New(bbolt.Config{ Database: "my_database.db", Bucket: "my-bucket", Reset: false, }) ``` ### Config ```go // Config defines the config for storage. type Config struct { // Database path // // Optional. Default is "fiber.db" Database string // Bbolt bucket name // // Optional. Default is "fiber_storage" Bucket string // Timeout is the amount of time to wait to obtain a file lock. // Only available on Darwin and Linux. // // Optional. Default is 60 * time.Second. Timeout time.Duration // Open database in read-only mode. // // Optional. Default is false ReadOnly bool // Reset clears any existing keys in existing Bucket // // Optional. Default is false Reset bool } ``` ### Default Config ```go // ConfigDefault is the default config var ConfigDefault = Config{ Database: "fiber.db", Bucket: "fiber_storage", Timeout: 60 * time.Second, ReadOnly: false, Reset: false, } ``` --- ## Cassandra A Cassandra storage driver using [gocql/gocql](https://github.com/gocql/gocql) ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=cassandra*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-cassandra.yml?label=Tests) ## Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) (*Storage, error) func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *gocql.Session ``` ### Installation Cassandra is supported on the latest two versions of Go: Install the cassandra implementation: ```bash go get github.com/gofiber/storage/cassandra ``` ### Running the tests This module uses [Testcontainers for Go](https://github.com/testcontainers/testcontainers-go/) to run integration tests, which will start a local instance of Cassandra as a Docker container under the hood. To run the tests, you must have Docker (or another container runtime 100% compatible with the Docker APIs) installed on your machine. ### Local development Before running this implementation, you must ensure a Cassandra cluster is available. For local development, we recommend using the Cassandra Docker image; it contains everything necessary for the client to operate correctly. To start Cassandra using Docker, issue the following: ```bash docker run --name cassandra -p 9042:9042 -d cassandra:latest ``` After running this command, you're ready to start using the storage and connecting to the database. ### Examples You can use the following options to create a cassandra storage driver: ```go import "github.com/gofiber/storage/cassandra" // Initialize default config, to connect to localhost:9042 using the memory engine and with a clean table. store := New(Config{ Hosts: []string{"localhost:9042"}, Keyspace: "test_keyspace_creation", Table: "test_kv", Expiration : 10 * time.Minute, }) ``` ### Config ```go // Config defines the configuration options for the Cassandra storage type Config struct { // Optional. Default is localhost // Hosts is a list of Cassandra nodes to connect to. Hosts []string // Optional. Default is gofiber // Keyspace is the name of the Cassandra keyspace to use. Keyspace string // Optional. Default is kv_store // Table is the name of the Cassandra table to use. Table string // Optional. Default is Quorum // Consistency is the Cassandra consistency level. Consistency gocql.Consistency // Optional. PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy()) // PoolConfig is the Cassandra connection pool configuration. PoolConfig *gocql.PoolConfig // Optional. Default is false // SslOpts is the SSL options for the Cassandra cluster. SslOpts *gocql.SslOptions // Optional. Default is 10 minutes // Expiration is the time after which an entry is considered expired. Expiration time.Duration // Optional. Default is false // Reset is a flag to reset the database. Reset bool // Optional. Default is 3 // MaxRetries is the maximum number of retries for a query. MaxRetries int // Optional. Default is 5 seconds // ConnectTimeout is the timeout for connecting to the Cassandra cluster. ConnectTimeout time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ Hosts: []string{"localhost:9042"}, Keyspace: "gofiber", Table: "kv_store", Consistency: gocql.Quorum, Reset: false, Expiration: 10 * time.Minute, MaxRetries: 3, ConnectTimeout: 5 * time.Second, SslOpts: nil, PoolConfig: &gocql.PoolConfig{ HostSelectionPolicy: gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy()), }, } ``` --- ## Clickhouse A Clickhouse storage driver using [https://github.com/ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go). ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=clickhouse*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-clickhouse.yml?label=Tests) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) (*Storage, error) func NewWithContext(ctx context.Context, configuration Config) (*Storage, error) func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *Session ``` ### Installation Clickhouse is supported on the last version of Go: Install the clickhouse implementation: ```bash go get github.com/gofiber/storage/clickhouse ``` ### Running the tests This module uses [Testcontainers for Go](https://github.com/testcontainers/testcontainers-go/) to run integration tests, which will start a local instance of Clickhouse as a Docker container under the hood. To run the tests, you must have Docker (or another container runtime 100% compatible with the Docker APIs) installed on your machine. ### Local development Before running this implementation, you must ensure a Clickhouse cluster is available. For local development, we recommend using the Clickhouse Docker image; it contains everything necessary for the client to operate correctly. To start Clickhouse using Docker, issue the following: ```bash docker run -d -p 9000:9000 --name some-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server ``` After running this command you're ready to start using the storage and connecting to the database. ### Examples You can use the following options to create a clickhouse storage driver: ```go import "github.com/gofiber/storage/clickhouse" // Initialize default config, to connect to localhost:9000 using the memory engine and with a clean table. store, err := clickhouse.New(clickhouse.Config{ Host: "localhost", Port: 9000, Clean: true, }) // Initialize custom config to connect to a different host/port and use custom engine and with clean table. store, err := clickhouse.New(clickhouse.Config{ Host: "some-ip-address", Port: 9000, Engine: clickhouse.MergeTree, Clean: true, }) // Initialize to connect with TLS enabled with your own tls.Config and with clean table. tlsConfig := config := &tls.Config{...} store, err := clickhouse.New(clickhouse.Config{ Host: "some-ip-address", Port: 9000, Clean: true, TLSConfig: tlsConfig, }) ``` ### Config ```go // Config defines configuration options for Clickhouse connection. type Config struct { // The host of the database. Ex: 127.0.0.1 Host string // The port where the database is supposed to listen to. Ex: 9000 Port int // The database that the connection should authenticate from Database string // The username to be used in the authentication Username string // The password to be used in the authentication Password string // The name of the table that will store the data Table string // The engine that should be used in the table Engine string // Should start a clean table, default false Clean bool // TLS configuration, default nil TLSConfig *tls.Config // Should the connection be in debug mode, default false Debug bool // The function to use with the debug config, default print function. It only works when debug is true Debugf func(format string, v ...any) } ``` ### Default Config ```go var DefaultConfig = Config{ Host: "localhost", Port: 9000, Engine: "Memory", Clean: false, } ``` --- ## Cloudflare KV ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=cloudflarekv*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-cloudflarekv.yml?label=Tests) A Cloudflare KV storage driver using [cloudflare/cloudflare-go](https://github.com/cloudflare/cloudflare-go). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *cloudflare.API ``` ### Installation ```bash go mod init github.com// ``` And then install the Cloudflare KV implementation: ```bash go get github.com/gofiber/storage/cloudflarekv ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/cloudflarekv" ``` You can use the following methods to create storage. The Key must be an API Token generated with at least `Account.Workers KV Storage` permission. Check the [Create API Token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) documentation to generate one. ```go // Initialize default config store := cloudflarekv.New() store := cloudflarekv.New(cloudflarekv.Config{ Key: "", Email: "", AccountID: "fiber", NamespaceID: "fiber", Reset: false, }) ``` ### Config ```go type Config struct { // Cloudflare Auth Token // // Optional. Default is "" Key string // Cloudflare Email // // Optional. Default is "" Email string // Account id // // Optional. Default is "fiber" AccountID string // Namespace id // // Optional. Default is "fiber" NamespaceID string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool } ``` ### Default Config ```go var ConfigDefault = Config{ Key: "", Email: "", AccountID: "fiber", NamespaceID: "fiber", Reset: false, } ``` --- ## Coherence A Coherence storage driver using [https://github.com/oracle/coherence-go-client](https://github.com/oracle/coherence-go-client). ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=coherence*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-coherence.yml?label=Tests) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) (*Storage, error) func NewWithContext(ctx context.Context, config ...Config) (*Storage, error) func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *Session ``` ### Installation Coherence is supported on Go versions 1.23 and above: Install the coherence implementation: ```bash go get github.com/gofiber/storage/coherence ``` Before running or testing this implementation, you must ensure a Coherence cluster is available. For local development, we recommend using the Coherence CE Docker image; it contains everything necessary for the client to operate correctly. To start a Coherence cluster using Docker, issue the following: ```bash docker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:25.03 ``` See the documentation [here](https://pkg.go.dev/github.com/oracle/coherence-go-client/v2@v2.0.0/coherence#hdr-Obtaining_a_Session) on connection options when creating a Coherence session. ### Examples Import the storage package. ```go import "github.com/gofiber/storage/coherence" ``` You can use the following possibilities to create a storage: ```go // Initialize default config, to connect to localhost:1408 using plain text store, err := coherence.New() // Initialize custom config to connect to a different host/port and use plain text and expiry of 5 minutes. store, err := coherence.New(coherence.Config{ Address: "my-host:myport", Expiration: time.Duration(300) * time.Second, // 5 minutes }) // Initialize to connect with TLS enabled with your own tls.Config tlsConfig := config := &tls.Config{...} store, err := coherence.New(coherence.Config{ Address: "my-host:myport", TLSConfig: tlsConfig, }) ``` > Note: If you create two stores using `coherence.New()` they will effectivity be identical. > If you wish to have two separate stores, then you can use: > ```go > store1, err := coherence.New(Config{ScopeName: "scope1"}) > store2, err := coherence.New(Config{ScopeName: "scope2"}) > ``` **Near Caches** The latest version of the Coherence Go client introduces near cache support to cache frequently accessed data in the Go client to avoid sending requests across the network. This is particularly useful if you are using sticky sessions via a LBR as this will cache the session in the Go process and the `Get()` operations will be much quicker. When the session is expired on the server it will automatically be removed from the near cache. To enable this for you session, you can set the `NearCacheTimeout` to a duration less than the expiry. ```go // Initialize default config, to connect to localhost:1408 using plain text store, err := coherence.New() // Use plain text with default expiry of 5 minutes, and a near cache expiry of 2 minutes store, err := coherence.New(coherence.Config{ Address: "my-host:myport", Expiration: time.Duration(300) * time.Second, // 5 minutes NearCacheTimeout: time.Duration(120) * time.Second, // 2 minutes }) ``` > Note: You must ensure your near cache timeout is less that the session timeout. ### Config ```go // Config defines configuration options for Coherence connection. type Config struct { // Address to connect to, defaults to "localhost:1408" Address string // Timeout is the default session timeout to connect to Coherence, defaults to 30s Timeout time.Duration // ScopeName defines a scope allowing for multiple storage sessions ScopeName string // Reset indicates if the store should be reset after being created Reset bool // TLSConfig specifies tls.Config to use when connecting, if nil then plain text is used TLSConfig *tls.Config // NearCacheTimeout defines the timeout for a near cache. Is this is set, then a near cache // with the timeout is created. Note: this must be less than the session timeout or any timeout you specify // when using Set(). NearCacheTimeout time.Duration } ``` ### Default Config ```go var DefaultConfig = Config{ Address: "localhost:1408", Timeout: time.Duration(120) * time.Seconds, ScopeName: defaultScopeName, Reset: false, NearCacheTimeout: time.Duration(60) * time.Seconds, } ``` --- ## Couchbase ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=couchbase*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-couchbase.yml?label=Tests) A Couchbase storage driver using [couchbase/gocb](https://github.com/couchbase/gocb). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *gocb.Cluster ``` ### Installation Couchbase is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the Couchbase implementation: ```bash go get github.com/gofiber/storage/couchbase/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/couchbase/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := couchbase.New() // Initialize Couchbase storage with custom config store := couchbase.New(couchbase.Config{ Host: "127.0.0.1:8091", Username: "", Password: "", Bucket: 0, ConnectionTimeout: 3* time.Second, KVTimeout: 1* time.Second, }) ``` ### Config ```go type Config struct { // The application username to Connect to the Couchbase cluster Username string // The application password to Connect to the Couchbase cluster Password string // The connection string for the Couchbase cluster Host string // The name of the bucket to Connect to Bucket string // The timeout for connecting to the Couchbase cluster ConnectionTimeout time.Duration // The timeout for performing operations on the Couchbase cluster KVTimeout time.Duration } ``` ### Default Config ```go // ConfigDefault is the default config var ConfigDefault = Config{ Host: "127.0.0.1:8091", Username: "admin", Password: "123456", Bucket: "fiber_storage", ConnectionTimeout: 3 * time.Second, KVTimeout: 1 * time.Second, } ``` --- ## DynamoDB ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=dynamodb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-dynamodb.yml?label=Tests) A DynamoDB storage driver using [aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2). **Note:** If config fields of credentials not given, credentials are using from the environment variables, ~/.aws/credentials, or EC2 instance role. If config fields of credentials given, credentials are using from config. Look at: [specifying credentials](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config Config) *Storage func NewWithContext(ctx context.Context, config Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *awsdynamodb.Client ``` ### Installation DynamoDB is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the dynamodb implementation: ```bash go get github.com/gofiber/storage/dynamodb/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/dynamodb/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize dynamodb store := dynamodb.New(dynamodb.Config{ }) ``` ### Config ```go type Config struct { // Region of the DynamoDB service you want to use. // Valid values: https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region. // E.g. "us-west-2". // Optional (read from shared config file or environment variable if not set). // Environment variable: "AWS_REGION". Region string // Name of the DynamoDB table. // Optional ("fiber_storage" by default). Table string // CustomEndpoint allows you to set a custom DynamoDB service endpoint. // This is especially useful if you're running a "DynamoDB local" Docker container for local testing. // Typical value for the Docker container: "http://localhost:8000". // See https://hub.docker.com/r/amazon/dynamodb-local/. // Optional ("" by default) Endpoint string // Credentials overrides AWS access key and AWS secret access key. Not recommended. // // Optional. Default is Credentials{} Credentials Credentials // The maximum number of times requests that encounter retryable failures should be attempted. // // Optional. Default is 3 MaxAttempts int // Reset clears any existing keys in existing Bucket // // Optional. Default is false Reset bool // ReadCapacityUnits of the table. // Only required when the table doesn't exist yet and is created by gokv. // Optional (5 by default, which is the same default value as when creating a table in the web console) // 25 RCUs are included in the free tier (across all tables). // For example calculations, see https://github.com/awsdocs/amazon-dynamodb-developer-guide/blob/c420420a59040c5b3dd44a6e59f7c9e55fc922ef/doc_source/HowItWorks.ProvisionedThroughput. // For limits, see https://github.com/awsdocs/amazon-dynamodb-developer-guide/blob/c420420a59040c5b3dd44a6e59f7c9e55fc922ef/doc_source/Limits.md#capacity-units-and-provisioned-throughput.md#provisioned-throughput. ReadCapacityUnits int64 // ReadCapacityUnits of the table. // Only required when the table doesn't exist yet and is created by gokv. // Optional (5 by default, which is the same default value as when creating a table in the web console) // 25 RCUs are included in the free tier (across all tables). // For example calculations, see https://github.com/awsdocs/amazon-dynamodb-developer-guide/blob/c420420a59040c5b3dd44a6e59f7c9e55fc922ef/doc_source/HowItWorks.ProvisionedThroughput. // For limits, see https://github.com/awsdocs/amazon-dynamodb-developer-guide/blob/c420420a59040c5b3dd44a6e59f7c9e55fc922ef/doc_source/Limits.md#capacity-units-and-provisioned-throughput.md#provisioned-throughput. WriteCapacityUnits int64 // If the table doesn't exist yet, gokv creates it. // If WaitForTableCreation is true, gokv will block until the table is created, with a timeout of 15 seconds. // If the table still doesn't exist after 15 seconds, an error is returned. // If WaitForTableCreation is false, gokv returns the client immediately. // In the latter case you need to make sure that you don't read from or write to the table before it's created, // because otherwise you will get ResourceNotFoundException errors. // Optional (true by default). WaitForTableCreation *bool } type Credentials struct { AccessKey string SecretAccessKey string } ``` ### Default Config ```go var ConfigDefault = Config{ Table: "fiber_storage", Credentials: Credentials{}, MaxAttempts: 3, Reset: false, ReadCapacityUnits: 5, WriteCapacityUnits: 5, WaitForTableCreation: aws.Bool(true), } ``` --- ## Etcd ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=etcd*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-etcd.yml?label=Tests) A Etcd storage driver using [`etcd-io/etcd`](https://github.com/etcd-io/etcd). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *clientv3.Client ``` ### Installation Etcd is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the etcd implementation: ```bash go get github.com/gofiber/storage/etcd/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/etcd/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := etcd.New() // Initialize custom config store := etcd.New(Config{ Endpoints: []string{"localhost:2379"}, }) ``` ### Config ```go type Config struct { // Endpoints is a list of URLs. Endpoints []string // DialTimeout is the timeout for failing to establish a connection. DialTimeout time.Duration // Username is a username for authentication. Username string // Password is a password for authentication. Password string // TLS holds the client secure credentials, if any. TLS *tls.Config } ``` ### Default Config ```go var ConfigDefault = Config{ Endpoints: []string{"localhost:2379"}, DialTimeout: 2 * time.Second, Username: "", Password: "", TLS: nil, } ``` --- ## Firestore ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=firestore*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-firestore.yml?label=Tests) A Firestore storage driver using [cloud.google.com/go/firestore](https://pkg.go.dev/cloud.google.com/go/firestore). **Note:** If no credentials are provided, the driver uses Application Default Credentials (ADC) or the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. If credentials are provided via config (`Credentials` or `CredentialsPath`), those take precedence. See: [Google Cloud Authentication Guide](https://cloud.google.com/docs/authentication) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) *Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func NewFromConnection(client *firestore.Client, collection string) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *firestore.Client ``` ### Installation Firestore is tested on the last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the firestore implementation: ```bash go get github.com/gofiber/storage/firestore ``` ### Examples Import the storage package. ```go import firestorage "github.com/gofiber/storage/firestore" ``` You can use the following possibilities to create a storage: ```go // Initialize with Application Default Credentials store := firestorage.New(firestorage.Config{ ProjectID: "my-gcp-project", }) // Initialize with service account JSON file store := firestorage.New(firestorage.Config{ ProjectID: "my-gcp-project", CredentialsPath: "/path/to/service-account-key.json", Collection: "sessions", }) // Initialize with embedded credentials JSON store := firestorage.New(firestorage.Config{ ProjectID: "my-gcp-project", Credentials: `{"type": "service_account", ...}`, }) // Initialize with custom timeout store := firestorage.New(firestorage.Config{ ProjectID: "my-gcp-project", Collection: "fiber_storage", RequestTimeout: 10 * time.Second, Reset: false, }) ``` #### Using an Existing Firestore Client If you already have a Firestore client configured in your application, you can create a Storage instance directly from that client: ```go import ( "cloud.google.com/go/firestore" "context" firestorage "github.com/gofiber/storage/firestore" ) ctx := context.Background() client, err := firestore.NewClient(ctx, "my-gcp-project") if err != nil { panic(err) } store := firestorage.NewFromConnection(client, "my_collection") ``` ### Config ```go type Config struct { // ProjectID is the Google Cloud project ID // Required. Will panic if empty ProjectID string // Collection name where data will be stored // // Optional. Default is "fiber_storage" Collection string // CredentialsPath is the path to the service account JSON key file // If not provided, Application Default Credentials (ADC) will be used // // Optional. Default is "" CredentialsPath string // Credentials is a JSON string with service account credentials // Takes precedence over CredentialsPath if both are provided // // Optional. Default is "" Credentials string // RequestTimeout is the timeout for Firestore requests // // Optional. Default is 10 seconds RequestTimeout time.Duration // Reset clears all documents in the collection on initialization // // Optional. Default is false Reset bool } ``` ### Default Config ```go var ConfigDefault = Config{ Collection: "fiber_storage", RequestTimeout: 10 * time.Second, Reset: false, } ``` ### Additional Resources - [Firestore Go SDK Documentation](https://pkg.go.dev/cloud.google.com/go/firestore) - [Firebase Console](https://console.firebase.google.com) - [Google Cloud Firestore Documentation](https://cloud.google.com/firestore/docs) - [Google Cloud Authentication Guide](https://cloud.google.com/docs/authentication) - [Firestore Quotas and Limits](https://cloud.google.com/firestore/quotas) --- ## LevelDB ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=leveldb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-leveldb.yml?label=Tests) A fast key-value DB using [syndtr/goleveldb](https://github.com/syndtr/goleveldb) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *leveldb.DB ``` **Note:** The context methods are dummy methods and don't have any functionality, as LevelDB does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation LevelDB is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the leveldb implementation: ```bash go get github.com/gofiber/storage/leveldb ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/leveldb" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := leveldb.New() // Initialize custom config store := leveldb.New(leveldb.Config{ Path: "./testdb", GCInterval: 10 * time.Second, }) ``` ### Config ```go type Config struct { // Path is the filesystem path for the database // // Optional. Default is "./fiber.leveldb" Path string // CacheSize is the size of LevelDB's cache (in MB) // // Optional. Default is 8MB CacheSize int // BlockSize is the size of data blocks (in KB) // // Optional. Default is 4KB BlockSize int // WriteBuffer is the size of write buffer (in MB) // // Optional. Default is 4MB WriteBuffer int // CompactionL0Trigger is the number of level-0 tables that triggers compaction // // Optional. Default is 4 CompactionL0Trigger int // WriteL0PauseTrigger is the number of level-0 tables that triggers write pause // // Optional. Default is 12 WriteL0PauseTrigger int // WriteL0SlowdownTrigger is the number of level-0 tables that triggers write slowdown // // Optional. Default is 8 WriteL0SlowdownTrigger int // MaxOpenFiles is the maximum number of open files that can be held // // Optional. Default is 200 on MacOS, 500 on others MaxOpenFiles int // CompactionTableSize is the size of compaction table (in MB) // // Optional. Default is 2MB CompactionTableSize int // BloomFilterBits is the number of bits used in bloom filter // // Optional. Default is 10 bits/key BloomFilterBits int // NoSync completely disables fsync // // Optional. Default is false NoSync bool // ReadOnly opens the database in read-only mode // // Optional. Default is false ReadOnly bool // ErrorIfMissing returns error if database doesn't exist // // Optional. Default is false ErrorIfMissing bool // ErrorIfExist returns error if database exists // // Optional. Default is false ErrorIfExist bool // GCInterval is the garbage collection interval // // Optional. Default is 10 minutes GCInterval time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ Path: "./fiber.leveldb", CacheSize: 8, // 8 MB BlockSize: 4, // 4 KB WriteBuffer: 4, // 4 MB CompactionL0Trigger: 4, WriteL0PauseTrigger: 12, WriteL0SlowdownTrigger: 8, MaxOpenFiles: func() int { if runtime.GOOS == "darwin" { return 200 // MacOS } return 500 // Unix/Linux }(), CompactionTableSize: 2, // 2 MB BloomFilterBits: 10, // 10 bits per key NoSync: false, ReadOnly: false, ErrorIfMissing: false, ErrorIfExist: false, GCInterval: 10 * time.Minute, } ``` --- ## Memcache ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=memcache*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-memcache.yml?label=Tests) A Memcache storage driver using [`bradfitz/gomemcache`](https://github.com/bradfitz/gomemcache). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *mc.Client ``` **Note:** The context methods are dummy methods and don't have any functionality, as Memcache does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Memory is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the memory implementation: ```bash go get github.com/gofiber/storage/memory/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/memcache" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := memcache.New() // Initialize custom config store := memcache.New(memcache.Config{ Servers: "localhost:11211", }) ``` ### Config ```go type Config struct { // Server list divided by , // i.e. server1:11211, server2:11212 // // Optional. Default is "127.0.0.1:11211" Servers string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool } ``` ### Default Config ```go var ConfigDefault = Config{ Servers: "127.0.0.1:11211", } ``` --- ## Memory ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=memory*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-memory.yml?label=Tests) An in-memory storage driver. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() map[string]entry func (s *Storage) Keys() ([][]byte, error) ``` **Note:** The context methods are dummy methods and don't have any functionality, as memory storage does not support context cancellation. They are provided for compliance with the Fiber storage interface. ### Installation Memory is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the memory implementation: ```bash go get github.com/gofiber/storage/memory/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/memory/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := memory.New() // Initialize custom config store := memory.New(memory.Config{ GCInterval: 10 * time.Second, }) ``` ### Config ```go type Config struct { // Time before deleting expired keys // // Default is 10 * time.Second GCInterval time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ GCInterval: 10 * time.Second, } ``` --- ## Minio(Minio) ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=minio*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-minio.yml?label=Tests) ## Minio A Minio storage driver using [minio/minio-go](https://github.com/minio/minio-go). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) *Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) CheckBucket() error func (s *Storage) CheckBucketWithContext(ctx context.Context) error func (s *Storage) CreateBucket() error func (s *Storage) CreateBucketWithContext(ctx context.Context) error func (s *Storage) RemoveBucket() error func (s *Storage) Conn() *minio.Client ``` ### Installation Install the Minio implementation: ```bash go get github.com/gofiber/storage/minio ``` And then run minio on Docker ```bash docker run -d --restart always -p 9000:9000 -p 9001:9001 --name storage-minio --volume=minio:/var/lib/minio -e MINIO_ROOT_USER='minio-user' -e MINIO_ROOT_PASSWORD='minio-password' minio/minio server --console-address ":9001" /var/lib/minio ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/minio" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := minio.New() // Initialize custom config store := minio.New(minio.Config{ Bucket: "fiber-bucket", Endpoint: "localhost:9000", Credentials: Credentials{ AccessKeyID: "minio-user", SecretAccessKey: "minio-password", }, }) ``` ### Config ```go // Config defines the config for storage. type Config struct { // Bucket // Default fiber-bucket Bucket string // Endpoint is a host name or an IP address Endpoint string // Region Set this value to override region cache // Optional Region string // Token Set this value to provide x-amz-security-token (AWS S3 specific) // Optional, Default is false Token string // Secure If set to true, https is used instead of http. // Default is false Secure bool // Reset clears any existing keys in existing Bucket // Optional. Default is false Reset bool // The maximum number of times requests that encounter retryable failures should be attempted. // Optional. Default is 10, same as the MinIO client. MaxRetry int // Credentials Minio access key and Minio secret key. // Need to be defined Credentials Credentials // GetObjectOptions Options for GET requests specifying additional options like encryption, If-Match GetObjectOptions minio.GetObjectOptions // PutObjectOptions // Allows user to set optional custom metadata, content headers, encryption keys and number of threads for multipart upload operation. PutObjectOptions minio.PutObjectOptions // ListObjectsOptions Options per to list objects ListObjectsOptions minio.ListObjectsOptions // RemoveObjectOptions Allows user to set options RemoveObjectOptions minio.RemoveObjectOptions } ``` ### Default Config The default configuration lacks Bucket, Region, and Endpoint which are all required and must be overwritten: ```go // ConfigDefault is the default config var ConfigDefault = Config{ Bucket: "fiber-bucket", Endpoint: "", Region: "", Token: "", Secure: false, Reset: false, Credentials: Credentials{}, GetObjectOptions: minio.GetObjectOptions{}, PutObjectOptions: minio.PutObjectOptions{}, ListObjectsOptions: minio.ListObjectsOptions{}, RemoveObjectOptions: minio.RemoveObjectOptions{}, } type Credentials struct { AccessKeyID string SecretAccessKey string } ``` --- ## MockStorage ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=mockstorage*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-mockstorage.yml?label=Tests) A mock storage implementation for Fiber. This storage is not persistent and is only used for testing purposes. ## Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ## Signatures ### Structs ```go type Storage struct { // contains filtered or unexported fields } type Entry struct { Value []byte Exp time.Time } type Config struct { CustomFuncs *CustomFuncs } type CustomFuncs struct { GetFunc func(key string) ([]byte, error) SetFunc func(key string, val []byte, exp time.Duration) error DeleteFunc func(key string) error ResetFunc func() error CloseFunc func() error ConnFunc func() map[string]Entry KeysFunc func() ([][]byte, error) } ``` ### Functions ```go // New creates a new Storage instance. You can optionally pass a Config. func New(config ...Config) *Storage // Get retrieves the value associated with the given key. func (s *Storage) Get(key string) ([]byte, error) // Set sets the value for the given key, with an optional expiration duration. func (s *Storage) Set(key string, val []byte, exp time.Duration) error // Delete removes the value associated with the given key. func (s *Storage) Delete(key string) error // Reset clears all values from the storage. func (s *Storage) Reset() error // Close performs any necessary cleanup when the storage is no longer needed. func (s *Storage) Close() error // Conn returns a copy of the current state of the storage. func (s *Storage) Conn() map[string]Entry // Keys returns a list of all keys in the storage. func (s *Storage) Keys() ([][]byte, error) // SetCustomFuncs allows you to set custom functions for the storage operations. func (s *Storage) SetCustomFuncs(custom *CustomFuncs) ``` ## Installation MockStorage is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the mockstorage implementation: ```bash go get github.com/gofiber/storage/mockstorage ``` ## Examples Import the storage package. ```go import "github.com/gofiber/storage/mockstorage" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := mockstorage.New() // Set a value in the storage. err := store.Set("key1", []byte("value1"), 0) if err != nil { // handle error } // Get a value from the storage. val, err := store.Get("key1") if err != nil { // handle error } fmt.Println(string(val)) // prints "value1" // Delete a value from the storage. err = store.Delete("key1") if err != nil { // handle error } // Mocking storage operations in tests: func TestMyFunction(t *testing.T) { // Create a new instance of MockStorage store := mockstorage.New() // Mock the Set function store.SetCustomFuncs(&mockstorage.CustomFuncs{ Set: func(key string, val []byte, exp time.Duration) error { if key == "expectedKey" && string(val) == "expectedValue" { return nil } return errors.New("unexpected key or value") }, }) // Call the function you want to test, which should call store.Set err := MyFunction(store) // Check that the function behaved as expected if err != nil { t.Errorf("MyFunction returned an error: %v", err) } } ``` > **Note:** In the `mockstorage` package, expiration of data is not handled automatically in the background. The data is only marked as expired and removed when you attempt to `Get()` it after its expiration time. If you're using a custom `Get()` function or accessing the data directly using the `Conn()` function, expired data will not be removed. Keep this in mind when writing your tests. ## Config ```go type Config struct { CustomFuncs *CustomFuncs } ``` ## Default Config ```go var ConfigDefault = Config{ CustomFuncs: &CustomFuncs{ GetFunc: nil, SetFunc: nil, DeleteFunc: nil, ResetFunc: nil, CloseFunc: nil, ConnFunc: nil, KeysFunc: nil, }, } ``` --- ## MongoDB(Mongodb) ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=mongodb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-mongodb.yml?label=Tests) A MongoDB storage driver using [mongodb/mongo-go-driver](https://github.com/mongodb/mongo-go-driver). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *mongo.Database ``` ### Installation MongoDB is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the mongodb implementation: ```bash go get github.com/gofiber/storage/mongodb/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/mongodb/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := mongodb.New() // Initialize custom config store := mongodb.New(mongodb.Config{ Host: "127.0.0.1", Port: 27017, Database: "fiber", Collection: "fiber_storage", Reset: false, }) // Initialize custom config using connection string store := mongodb.New(mongodb.Config{ ConnectionURI: "mongodb://user:password@127.0.0.1:27017", Database: "fiber", Collection: "fiber_storage", Reset: false, }) ``` ### Config ```go type Config struct { // Connection string to use for DB. Will override all other authentication values if used // // Optional. Default is "" ConnectionURI string // Host name where the DB is hosted // // Optional. Default is "127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 27017 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Database name // // Optional. Default is "fiber" Database string // Collection name // // Optional. Default is "fiber_storage" Collection string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool } ``` ### Default Config ```go var ConfigDefault = Config{ ConnectionURI: "", Host: "127.0.0.1", Port: 27017, Database: "fiber", Collection: "fiber_storage", Reset: false, } ``` --- ## MSSQL ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=mssql*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-mssql.yml?label=Tests) A MSSQL storage driver using [microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *sql.DB ``` ### Installation MSSQL is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the mssql implementation: ```bash go get github.com/gofiber/storage/mssql/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/mssql/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := mssql.New() // Initialize custom config store := mssql.New(mssql.Config{ Host: "127.0.0.1", Port: 1433, Database: "fiber", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, SslMode: "disable", }) // Initialize custom config using connection string store := mssql.New(mssql.Config{ ConnectionURI: "sqlserver://user:password@localhost:1433?database=fiber" Reset: false, GCInterval: 10 * time.Second, }) ``` ### Config ```go // Config defines the config for storage. type Config struct { // Connection string to use for DB. Will override all other authentication values if used // // Optional. Default is "" ConnectionURI string // Host name where the DB is hosted // // Optional. Default is "127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 1433 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Instance name // // Optional. Default is "" Instance string // Database name // // Optional. Default is "fiber" Database string // Table name // // Optional. Default is "fiber_storage" Table string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration // The SSL mode for the connection // // Optional. Default is "disable" SslMode string } ``` ### Default Config ```go var ConfigDefault = Config{ ConnectionURI: "", Host: "127.0.0.1", Port: 1433, Database: "fiber", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, SslMode: "disable", } ``` --- ## MySQL(Mysql) ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=mysql*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-mysql.yml?label=Tests) A MySQL storage driver using `database/sql` and [go-sql-driver/mysql](https://github.com/go-sql-driver/mysql). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *sql.DB ``` ### Installation MySQL is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the mysql implementation: ```bash go get github.com/gofiber/storage/mysql/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/mysql/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := mysql.New() // Initialize custom config store := mysql.New(mysql.Config{ Host: "127.0.0.1", Port: 3306, Database: "fiber", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, }) // Initialize custom config using connection string store := mysql.New(mysql.Config{ ConnectionURI: ":@tcp(:)/" Reset: false, GCInterval: 10 * time.Second, }) // Initialize custom config using sql db connection db, _ := sql.Open("mysql", ":@tcp(:)/") store := mysql.New(mysql.Config{ Db: db, Reset: false, GCInterval: 10 * time.Second, }) ``` ### Config ```go type Config struct { // DB Will override ConnectionURI and all other authentication values if used // // Optional. Default is nil Db *sql.DB // Connection string to use for DB. Will override all other authentication values if used // // Optional. Default is "" ConnectionURI string // Host name where the DB is hosted // // Optional. Default is "127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 3306 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Database name // // Optional. Default is "fiber" Database string // Table name // // Optional. Default is "fiber_storage" Table string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ ConnectionURI: "", Host: "127.0.0.1", Port: 3306, Database: "fiber", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, } ``` --- ## Nats ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=nats*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-nats.yml?label=Tests) A NATS Key/Value storage driver. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() (*nats.Conn, jetstream.KeyValue) func (s *Storage) Keys() ([]string, error) ``` ### Installation [NATS Key/Value Store](https://docs.nats.io/nats-concepts/jetstream/key-value-store) driver is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the nats implementation: ```bash go get github.com/gofiber/storage/nats ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/nats" ``` You can use the following options to create a storage driver: ```go // Initialize default config store := nats.New() // Initialize custom config store := nats.New(Config{ URLs: "nats://127.0.0.1:4443", NatsOptions: []nats.Option{ nats.MaxReconnects(2), // Enable TLS by specifying RootCAs nats.RootCAs("./testdata/certs/ca.pem"), }, KeyValueConfig: jetstream.KeyValueConfig{ Bucket: "test", Storage: jetstream.MemoryStorage, }, }) ``` ### Config ```go type Config struct { // Nats URLs, default "nats://127.0.0.1:4222". Can be comma separated list for multiple servers URLs string // Nats connection options. See nats_test.go for an example of how to use this. NatsOptions []nats.Option // Nats connection name ClientName string // Nats context Context context.Context // Nats key value config KeyValueConfig jetstream.KeyValueConfig // Wait for connection to be established, default: 100ms WaitForConnection time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ URLs: nats.DefaultURL, Context: context.Background(), ClientName: "fiber_storage", KeyValueConfig: jetstream.KeyValueConfig{ Bucket: "fiber_storage", }, WaitForConnection: 100 * time.Millisecond, } ``` --- ## Neo4j(Neo4j) ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=neo4j*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-neo4j.yml?label=Tests) A Neo4j storage driver using [neo4j/neo4j-go-driver](https://github.com/neo4j/neo4j-go-driver). > **Note: Requires latest two releases of Golang** ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) *Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() neo4j.DriverWithContext ``` ### Installation Neo4j is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the neo4j implementation: ```bash go get github.com/gofiber/storage/neo4j ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/neo4j" ``` You can use the following possibilities to create a storage: > The `neo4j` package name used in this example is the package name (and default import name) for this storage driver. Feel free import it with a custom name to avoid confusing it with the neo4j-go-driver package which also uses `neo4j` as package name (and default import name). ```go // Initialize default config store := neo4j.New() // Initialize custom config store := neo4j.New(neo4j.Config{ DB: driver, Node: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, }) ``` ### Config > The `neo4j`, `auth`, and `config` package names used here belong to the neo4j-go-driver package. ```go // Config defines the config for storage. type Config struct { // Connection pool // // DB neo4j.DriverWithContext object will override connection URI and other connection fields. // // Optional. Default is nil. DB neo4j.DriverWithContext // Target Server // // Optional. Default is "neo4j://localhost" URI string // Connection authentication // // Auth auth.TokenManager will override Username and Password fields // // Optional. Default is nil. Auth auth.TokenManager // Connection configurations // // Optional. Default is nil Configurations []func(*config.Config) // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Node name // // Optional. Default is "fiber_storage" Node string // Reset clears any existing keys (Nodes) // // Optional. Default is false Reset bool // Time before deleting expired keys (Nodes) // // Optional. Default is 10 * time.Second GCInterval time.Duration } ``` #### A note on Authentication If auth is enabled on your server, then authentication must be provided in one of the three ways (the previous overrides the next): - Via the connection pool, `neo4j.DriverWithContext`, provided on the `DB` field. - Via the `Auth` field: it must be an `auth.TokenManager` whose value is any one but `neo4j.NoAuth()`. - By setting both `Username` and `Password` fields: This will cause this storage driver to use Basic Auth. Otherwise, your neo4j driver will panic with authorization error. In contrast, if auth is disabled on your server, there's no need to provide any authentication parameter. ### Default Config Used only for optional fields ```go var ConfigDefault = Config{ URI: "neo4j://localhost", Node: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, } ``` --- ## Pebble ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=pebble*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-pebble.yml?label=Tests) A fast key-value DB using [cockroachdb/pebble](https://github.com/cockroachdb/pebble) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *pebble.DB ``` **Note:** The context methods are dummy methods and don't have any functionality, as Pebble does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Pebble is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` Note: This step is only required if you don't have an existing module. And then install the Pebble implementation: ```bash go get github.com/gofiber/storage/pebble/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/pebble/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := pebble.New() // Initialize custom config store := pebble.New(pebble.Config{ Path: "db", WriteOptions: &pebble.WriteOptions{}, }) ``` ### Config ```go type Config struct { // Database name // // Optional. Default is "./db" Path string // Pass write options during write operations // // Optional. Default is nil WriteOptions &pebble.WriteOptions{} } ``` ### Default Config ```go var ConfigDefault = Config{ Path: "db", WriteOptions: &pebble.WriteOptions{}, } ``` --- ## Postgres ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=postgres*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-postgres.yml?label=Tests) A Postgres storage driver using [jackc/pgx](https://github.com/jackc/pgx). > **CockroachDB** is also supported. Since CockroachDB is wire-compatible with PostgreSQL, this driver works with CockroachDB without any changes. Simply point the connection string to your CockroachDB instance. ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *pgxpool.Pool ``` ### Installation Postgres is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the postgres implementation: ```bash go get github.com/gofiber/storage/postgres/v3 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/postgres/v3" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := postgres.New() // Initialize custom config store := postgres.New(postgres.Config{ DB: dbPool, Table: "fiber_storage", Reset: false, Unlogged: false, GCInterval: 10 * time.Second, }) ``` ### Config ```go // Config defines the config for storage. type Config struct { // DB pgxpool.Pool object will override connection uri and other connection fields // // Optional. Default is nil DB *pgxpool.Pool // Connection string to use for DB. Will override all other authentication values if used // // Optional. Default is "" ConnectionURI string // Host name where the DB is hosted // // Optional. Default is "127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 5432 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Database name // // Optional. Default is "fiber" Database string // Table name // // Optional. Default is "fiber_storage" Table string // The SSL mode for the connection // // Optional. Default is "disable" SSLMode string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // Unlogged creates the table as UNLOGGED. // Applies only when the table is created. // // Optional. Default is false // Warning: UNLOGGED tables are not crash-safe and are not replicated. Unlogged bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration } ``` ### Default Config ```go // ConfigDefault is the default config var ConfigDefault = Config{ ConnectionURI: "", Host: "127.0.0.1", Port: 5432, Database: "fiber", Table: "fiber_storage", SSLMode: "disable", Reset: false, Unlogged: false, GCInterval: 10 * time.Second, } ``` --- ## Redis ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=redis*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-redis.yml?label=Tests) A Redis storage driver using [go-redis/redis](https://github.com/go-redis/redis). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func NewFromConnection(conn redis.UniversalClient) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() redis.UniversalClient func (s *Storage) Keys() ([][]byte, error) ``` ### Installation Redis is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: > **Note:** You can also use [DragonflyDB](https://dragonflydb.io/) as a Redis replacement. > Since DragonflyDB is fully compatible with the Redis API, you can use it exactly like Redis **without any code changes**. > [Example](#example-using-dragonflydb) ```bash go mod init github.com// ``` And then install the redis implementation: ```bash go get github.com/gofiber/storage/redis/v3 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/redis/v3" ``` You can use the one of the following options to create a Redis Storage: ```go // Initialize default config store := redis.New() // Initialize custom config store := redis.New(redis.Config{ Host: "127.0.0.1", Port: 6379, Username: "", Password: "", Database: 0, Reset: false, TLSConfig: nil, PoolSize: 10 * runtime.GOMAXPROCS(0), }) // Initialize Redis Failover Client store := redis.New(redis.Config{ MasterName: "master-name", Addrs: []string{":6379"}, }) // Initialize Redis Cluster Client store := redis.New(redis.Config{ Addrs: []string{":6379", ":6380"}, }) // Initialize AWS ElastiCache Redis Cluster with Configuration Endpoint store := redis.New(redis.Config{ Addrs: []string{"cluster.xxxxx.cache.amazonaws.com:6379"}, IsClusterMode: true, }) // Create a client with support for TLS cer, err := tls.LoadX509KeyPair("./client.crt", "./client.key") if err != nil { log.Println(err) return } tlsCfg := &tls.Config{ MinVersion: tls.VersionTLS12, InsecureSkipVerify: true, Certificates: []tls.Certificate{cer}, } store = redis.New(redis.Config{ URL: "redis://:@127.0.0.1:6379/", TLSConfig: tlsCfg, Reset: false, }) // Create a client with a Redis URL with all information. store = redis.New(redis.Config{ URL: "redis://:@127.0.0.1:6379/", Reset: false, }) ``` ### Config ```go type Config struct { // Host name where the DB is hosted // // Optional. Default is "127.0.0.1" Host string // Port where the DB is listening on // // Optional. Default is 6379 Port int // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // Database to be selected after connecting to the server. // // Optional. Default is 0 Database int // URL standard format Redis URL. If this is set all other config options, Host, Port, Username, Password, Database have no effect. // // Example: redis://:@localhost:6379/ // Optional. Default is "" URL string // Either a single address or a seed list of host:port addresses, this enables FailoverClient and ClusterClient // // Optional. Default is []string{} Addrs []string // MasterName is the sentinel master's name // // Optional. Default is "" MasterName string // ClientName will execute the `CLIENT SETNAME ClientName` command for each conn. // // Optional. Default is "" ClientName string // SentinelUsername // // Optional. Default is "" SentinelUsername string // SentinelPassword // // Optional. Default is "" SentinelPassword string // Reset clears any existing keys in existing Collection // // Optional. Default is false Reset bool // TLS Config to use. When set TLS will be negotiated. // // Optional. Default is nil TLSConfig *tls.Config // Maximum number of socket connections. // // Optional. Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS. PoolSize int // IsClusterMode forces cluster mode even with single address. // Useful for AWS ElastiCache Configuration Endpoints. // // Optional. Default is false IsClusterMode bool } ``` ### Default Config ```go var ConfigDefault = Config{ Host: "127.0.0.1", Port: 6379, Username: "", Password: "", URL: "", Database: 0, Reset: false, TLSConfig: nil, PoolSize: 10 * runtime.GOMAXPROCS(0), Addrs: []string{}, MasterName: "", ClientName: "", SentinelUsername: "", SentinelPassword: "", IsClusterMode: false, } ``` ### Using an Existing Redis Connection If you already have a Redis client configured in your application, you can create a Storage instance directly from that client. This is useful when you want to share an existing connection throughout your application instead of creating a new one. ```go import ( "github.com/gofiber/storage/redis" redigo "github.com/redis/go-redis/v9" "fmt" "context" ) func main() { // Create or reuse a Redis universal client (e.g., redis.NewClient, redis.NewClusterClient, etc.) client := redigo.NewUniversalClient(&redigo.UniversalOptions{ Addrs: []string{"127.0.0.1:6379"}, }) // Create a new Storage instance from the existing Redis client store := redis.NewFromConnection(client) // Set a value if err := store.Set("john", []byte("doe"), 0); err != nil { panic(err) } // Get the value val, err := store.Get("john") if err != nil { panic(err) } fmt.Println("Stored value:", string(val)) // Clean up store.Close() } ``` ### Example: Using DragonflyDB > **Note:** You can use [DragonflyDB](https://dragonflydb.io/) in the same way as Redis. > Simply start a DragonflyDB server and configure it just like Redis. Then, call `New()` and use it exactly as you would with Redis. --- ## Ristretto ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=ristretto*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-ristretto.yml?label=Tests) A Memory-bound storage driver using [`dgraph-io/ristretto`](https://github.com/dgraph-io/ristretto). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *ristretto.Cache ``` **Note:** The context methods are dummy methods and don't have any functionality, as Ristretto does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation Ristretto is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the ristretto implementation: ```bash go get github.com/gofiber/storage/ristretto/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/ristretto/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := ristretto.New() // Initialize custom config store := ristretto.New(ristretto.Config{ NumCounters: 1e7, // number of keys to track frequency of (10M). MaxCost: 1 << 30, // maximum cost of cache (1GB). BufferItems: 64, // number of keys per Get buffer. }) ``` ### Config ```go type Config struct { // NumCounters number of keys to track frequency of (10M). NumCounters int64 // MaxCost maximum cost of cache (1GB). MaxCost int64 // BufferItems number of keys per Get buffer. BufferItems int64 } ``` ### Default Config ```go var ConfigDefault = Config{ NumCounters: 1e7, MaxCost: 1 << 30, BufferItems: 64, DefaultCost: 1, } ``` --- ## Rueidis ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=rueidis*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-rueidis.yml?label=Tests) A fast Redis Storage that does auto pipelining and supports client side caching. [redis/rueidis](https://github.com/redis/rueidis). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() rueidis.Client ``` ### Installation Rueidis is tested on the latest [Go version](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the rueidis implementation: ```bash go get github.com/gofiber/storage/rueidis ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/rueidis" ``` You can use the one of the following options to create a Rueidis Storage: ```go // Initialize default config (localhost:6379) store := rueidis.New() // Initialize custom config store := rueidis.New(rueidis.Config{ InitAddress: []string{"localhost:6380"}, Username: "", Password: "", Database: 0, Reset: false, TLSConfig: nil, }) // Initialize using Rueidis URL store := rueidis.New(rueidis.Config{ URL: "redis://localhost:6379", }) // Initialize Rueidis Cluster Client store := rueidis.New(rueidis.Config{ InitAddress: []string{":6379", ":6380"}, }) // Create a client with support for TLS cer, err := tls.LoadX509KeyPair("./client.crt", "./client.key") if err != nil { log.Println(err) return } tlsCfg := &tls.Config{ MinVersion: tls.VersionTLS12, InsecureSkipVerify: true, Certificates: []tls.Certificate{cer}, } store = rueidis.New(rueidis.Config{ InitAddress: []string{"localhost:6380"}, Username: "", Password: "", SelectDB: 0, TLSConfig: tlsCfg, }) ``` ### Config ```go type Config struct { // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // ClientName will execute the `CLIENT SETNAME ClientName` command for each conn. // // Optional. Default is "" ClientName string // URL standard format Redis URL. If this is set all other config options, InitAddress, Username, Password, ClientName, and SelectDB have no effect. // // Example: redis://:@localhost:6379/ // Optional. Default is "" URL string // SelectDB to be selected after connecting to the server. // // Optional. Default is 0 SelectDB int // Either a single address or a seed list of host:port addresses, this enables FailoverClient and ClusterClient // // Optional. Default is []string{"127.0.0.1:6379"} InitAddress []string // TLS Config to use. When set TLS will be negotiated. // // Optional. Default is nil TLSConfig *tls.Config // CacheSizeEachConn is redis client side cache size that bind to each TCP connection to a single redis instance. // // Optional. The default is DefaultCacheBytes: 128 * (1 << 20) CacheSizeEachConn int // RingScaleEachConn sets the size of the ring buffer in each connection to (2 ^ RingScaleEachConn). // // Optional. The default is RingScaleEachConn, which results into having a ring of size 2^10 for each connection. RingScaleEachConn int // ReadBufferEachConn is the size of the bufio.NewReaderSize for each connection, default to DefaultReadBuffer (0.5 MiB). // // Optional. The default is DefaultReadBuffer: 1 << 19 ReadBufferEachConn int // WriteBufferEachConn is the size of the bufio.NewWriterSize for each connection, default to DefaultWriteBuffer (0.5 MiB). // // Optional. The default is DefaultWriteBuffer: 1 << 19 WriteBufferEachConn int // BlockingPoolSize is the size of the connection pool shared by blocking commands (ex BLPOP, XREAD with BLOCK). // // Optional. The default is DefaultPoolSize: 1000 BlockingPoolSize int // PipelineMultiplex determines how many tcp connections used to pipeline commands to one redis instance. // // Optional. The default for single and sentinel clients is 2, which means 4 connections (2^2). PipelineMultiplex int // DisableRetry disables retrying read-only commands under network errors // // Optional. The default is False DisableRetry bool // DisableCache falls back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti // // Optional. The default is false DisableCache bool // AlwaysPipelining makes rueidis.Client always pipeline redis commands even if they are not issued concurrently. // // Optional. The default is true AlwaysPipelining bool // Reset clears any existing keys in existing Collection // // Optional. Default is false Reset bool // CacheTTL TTL // // Optional. Default is time.Minute CacheTTL time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ Username: "", Password: "", ClientName: "", SelectDB: 0, InitAddress: []string{"127.0.0.1:6379"}, TLSConfig: nil, CacheSizeEachConn: rueidis.DefaultCacheBytes, RingScaleEachConn: rueidis.DefaultRingScale, ReadBufferEachConn: rueidis.DefaultReadBuffer, WriteBufferEachConn: rueidis.DefaultWriteBuffer, BlockingPoolSize: rueidis.DefaultPoolSize, PipelineMultiplex: 2, DisableRetry: false, DisableCache: false, AlwaysPipelining: true, Reset: false, CacheTTL: time.Minute, } ``` --- ## S3 ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=s3*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-s3.yml?label=Tests) A S3 storage driver using [aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2). **Note:** If config fields of credentials not given, credentials are using from the environment variables, ~/.aws/credentials, or EC2 instance role. If config fields of credentials given, credentials are using from config. Look at: [specifying credentials](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *s3.Client // Additional useful methods. func (s *Storage) CreateBucket(bucket string) error func (s *Storage) DeleteBucket(bucket string) error func (s *Storage) DeleteMany(keys ...string) error func (s *Storage) SetWithChecksum(key string, val []byte, checksum map[types.ChecksumAlgorithm][]byte) error ``` ### Installation S3 is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the s3 implementation: ```bash go get github.com/gofiber/storage/s3/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/s3/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := s3.New() // Initialize custom config store := s3.New(s3.Config{ Bucket: "my-bucket-url", Endpoint: "my-endpoint", Region: "my-region", Reset: false, }) ``` Create an object with `Set()`: ```go err := store.Set("my-key", []byte("my-value")) ``` Or, call `SetWithChecksum()` to create an object with checksum to ask S3 server to verify data integrity on server side: > Currently 4 algorithms are supported: > - types.ChecksumAlgorithmCrc32 (`CRC32`) > - types.ChecksumAlgorithmCrc32c (`CRC32C`) > - types.ChecksumAlgorithmSha1 (`SHA1`) > - types.ChecksumAlgorithmSha256 (`SHA256`) > > For more information, see [PutObjectInput](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#PutObjectInput). ```go key := "my-key" val := []byte("my-value") hash := sha256.New() hash.Write(val) sha256sum := hash.Sum(nil) // import "github.com/aws/aws-sdk-go-v2/service/s3/types" checksum = map[types.ChecksumAlgorithm][]byte{ types.ChecksumAlgorithmSha256: sha256sum, } err := store.SetWithChecksum(key, val, checksum) ``` ### Config ```go // Config defines the config for storage. type Config struct { // S3 bucket name Bucket string // AWS endpoint Endpoint string // AWS region Region string // Request timeout // // Optional. Default is 0 (no timeout) RequestTimeout time.Duration // Reset clears any existing keys in existing Bucket // // Optional. Default is false Reset bool // Credentials overrides AWS access key and AWS secret access key. Not recommended. // // Optional. Default is Credentials{} Credentials Credentials // The maximum number of times requests that encounter retryable failures should be attempted. // // Optional. Default is 3 MaxAttempts int } type Credentials struct { AccessKey string SecretAccessKey string } ``` ### Default Config The default configuration lacks Bucket, Region, and Endpoint which are all required and must be overwritten: ```go // ConfigDefault is the default config var ConfigDefault = Config{ Bucket: "", Region: "", Endpoint: "", Credentials: Credentials{}, MaxAttempts: 3, RequestTimeout: 0, Reset: false, } ``` --- ## ScyllaDb ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=scylladb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-scylladb.yml?label=Tests) # ScyllaDb A ScyllaDb storage engine for [Fiber](https://github.com/gofiber/fiber) using [gocql](https://github.com/scylladb/gocql). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Set(key string, value []byte, expire time.Duration) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Delete(key string) error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Reset() error func (s *Storage) Close() error func (s *Storage) Conn() *gocql.Session ``` ### Installation ScyllaDb is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the scylladb implementation: ```bash go get github.com/gofiber/storage/scylladb ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/scylladb" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := scylladb.New() // Initialize custom config store := scylladb.New(scylladb.Config{ Keyspace: "fiber", Hosts: []string{"127.0.0.1"}, Port: 9042, Table: "fiber_storage", Consistency: "ONE", Reset: false, }) // Initialize with support for TLS (SslOptions configures TLS use) // // InsecureSkipVerify and EnableHostVerification interact as follows: // // |Config.InsecureSkipVerify | EnableHostVerification | Result | // |--------------------------|------------------------|--------------------| // |Config is nil | false | do not verify host | // |Config is nil | true | verify host | // |false | false | verify host | // |true | false | do not verify host | // |false | true | verify host | // |true | true | verify host | store := New( Config{ Keyspace: "fiber", Hosts: []string{"127.0.0.1"}, Port: 9042, Table: "fiber_storage", Consistency: "ONE", SslOpts: &gocql.SslOptions{ Config: &tls.Config{ InsecureSkipVerify: false, // Set this too false to enable certificate verification }, CertPath: "/path/to/client_cert.pem", // Path to the client certificate KeyPath: "/path/to/client_key.pem", // Path to the client certificate's private key CaPath: "/path/to/ca_cert.pem", // Path to the CA certificate EnableHostVerification: true, // Enable hostname verification }, Reset: false, }, ) // Initialize custom config using scylladb connection cluster, _ := gocql.NewCluster("127.0.0.1") cluster.Keyspace = "fiber" cluster.Port = 9042 session, _ := cluster.CreateSession() store := scylladb.New(scylladb.Config{ Session: session, Keyspace: "fiber", Table: "fiber_storage", Reset: false, }) ``` ### Config ```go type Config struct { // Session is provided by the user to use an existing ScyllaDb session // Session Will override Keyspace and all other authentication values if used // // Optional. Default is nil Session *gocql.Session // Keyspace name // // Optional. Default is "fiber" Keyspace string // Hosts are an array of network addresses for establishing initial connections // You have the flexibility to specify one or multiple addresses as needed // // Optional. Default is "127.0.0.1" Hosts []string // Port where the ScyllaDb cluster is listening on // // Optional. Default is 9042 Port int // Username for ScyllaDb cluster // // Optional. Default is "" Username string // Password for ScyllaDb cluster // // Optional. Default is "" Password string // Table name // // Optional. Default is "fiber_storage" Table string // Level of the consistency // // Optional. Default is "LOCAL_ONE" Consistency string // SslOpts configures TLS use. // // Optional. Default is nil SslOpts *gocql.SslOptions // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // DisableInitialHostLookup disables the initial host lookup // // Optional. Default is false DisableInitialHostLookup bool } ``` ### Default Config ```go // ConfigDefault is the default config var ConfigDefault = Config{ Session: nil, Keyspace: "fiber", Hosts: []string{"127.0.0.1"}, Username: "", Password: "", Port: 9042, Table: "fiber_storage", Consistency: "LOCAL_ONE", SslOpts: nil, Reset: false, DisableInitialHostLookup: false, } ``` --- ## SQLite3 ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=sqlite3*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-sqlite3.yml?label=Tests) A SQLite3 storage driver using [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *sql.DB ``` ### Installation SQLite3 is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the sqlite3 implementation: ```bash go get github.com/gofiber/storage/sqlite3/v2 ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/sqlite3/v2" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := sqlite3.New() // Initialize custom config store := sqlite3.New(sqlite3.Config{ Database: "./fiber.sqlite3", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, MaxOpenConns: 100, MaxIdleConns: 100, ConnMaxLifetime: 1 * time.Second, }) ``` ### Config ```go type Config struct { // Database name // // Optional. Default is "fiber" Database string // Table name // // Optional. Default is "fiber_storage" Table string // Reset clears any existing keys in existing Table // // Optional. Default is false Reset bool // Time before deleting expired keys // // Optional. Default is 10 * time.Second GCInterval time.Duration // ////////////////////////////////// // Adaptor related config options // // ////////////////////////////////// // MaxIdleConns sets the maximum number of connections in the idle connection pool. // // Optional. Default is 100. MaxIdleConns int // MaxOpenConns sets the maximum number of open connections to the database. // // Optional. Default is 100. MaxOpenConns int // ConnMaxLifetime sets the maximum amount of time a connection may be reused. // // Optional. Default is 1 second. ConnMaxLifetime time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ Database: "./fiber.sqlite3", Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, MaxOpenConns: 100, MaxIdleConns: 100, ConnMaxLifetime: 1 * time.Second, } ``` --- ## SurrealDB ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=surrealdb*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-surrealdb.yml?label=Tests) ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) *Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() *surrealdb.DB func (s *Storage) List() ([]byte, error) { ``` **Note:** The context methods are dummy methods and don't have any functionality, as SurrealDB does not support context cancellation in its client library. They are provided for compliance with the Fiber storage interface. ### Installation SurrealDB is tested on latest two version of Golang. Make sure to initialize a Go module first if you haven’t already: ```bash go get github.com/gofiber/storage/surrealdb ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/surrealdb" ``` You can use the following possibilities to create a storage: ```go // Initialize default config store := surrealdb.New() // Initialize SurrealDB storage with custom config store := surrealdb.New(Config{ ConnectionString: "ws://localhost:8000", Namespace: "fiber_storage", Database: "fiber_storage", Username: "root", Password: "root", Access: "full", Scope: "all", DefaultTable: "fiber_storage", GCInterval: time.Second * 10, }) ``` ### Config ```go type Config struct { // The connection URL to connect to SurrealDB ConnectionString string // The namespace to be used in SurrealDB Namespace string // The database to be used within the specified namespace Database string // The application username to connect to SurrealDB Username string // The application password to connect to SurrealDB Password string // Optional access token or access type Access string // Optional scope for scoped logins (e.g., user-defined scopes) Scope string // The default table used to store key-value records DefaultTable string // Optional. Default is 10 * time.Second GCInterval time.Duration } ``` ### Default Config ```go // ConfigDefault is the default config var ConfigDefault = Config{ ConnectionString: "ws://localhost:8000", Namespace: "fiber_storage", Database: "fiber_storage", Username: "root", Password: "root", Access: "full", Scope: "all", DefaultTable: "fiber_storage", GCInterval: time.Second * 10, } ``` --- ## Test Helpers This directory hosts reusable utilities for exercising storage implementations in end-to-end and compatibility testing scenarios. Each helper is maintained as its own Go module so it can be consumed independently. ## Available helpers - **Redis** (`testhelpers/redis`): Spins up Redis with Testcontainers and exposes convenience functions for running integration tests against Redis-backed storage implementations. - **Test Compatibility Kit (TCK)** (`testhelpers/tck`): Provides a reusable test suite that validates any `storage.Storage` implementation for correctness and API parity. ## Running tests locally From the repository root, run tests for a helper by changing into its directory and executing `go test`: ```sh cd testhelpers/redis go test ./... -v -race ``` The helpers rely on Docker via [testcontainers-go](https://github.com/testcontainers/testcontainers-go), so ensure Docker is available and running before executing the tests. --- ## Redis Test Helper This module provides utilities for starting a disposable Redis instance with [testcontainers-go](https://github.com/testcontainers/testcontainers-go). It is useful for integration tests against storage implementations that rely on Redis. ## Features - Starts Redis containers with optional TLS, host/port, address, or URL connection helpers. - Supports container reuse via `WithReuse` for faster local iteration. - Exposes connection details (URL, host/port, addresses, TLS config) through the returned `Container` struct. ## Usage Import the helper and start a Redis container in your tests: ```go import ( testredis "github.com/gofiber/storage/testhelpers/redis" ) func TestExample(t *testing.T) { ctr := testredis.Start(t, "redis:7-alpine") // Use ctr.URL, ctr.Host/Port, or ctr.TLSConfig in your test code. } ``` ## Running locally From the repository root, execute the helper's tests: ```sh cd testhelpers/redis go test ./... -v -race ``` Docker must be available and running for the tests to start Redis containers. --- ## Test Compatibility Kit (TCK) for Storage Implementations The Test Compatibility Kit (TCK) is a standardized test suite for validating storage implementations in the Fiber Storage repository. It provides a comprehensive set of tests that ensure all storage backends behave consistently and correctly implement the `storage.Storage` interface. ## Overview The TCK leverages [testify/suite](https://github.com/stretchr/testify#suite-package) to provide a structured testing approach with setup/teardown hooks and consistent test execution. It automatically tests all core storage operations including: - Basic CRUD operations (Set, Get, Delete) - Context-aware operations (SetWithContext, GetWithContext, etc.) - TTL (Time-To-Live) functionality - Storage reset and cleanup - Connection handling for stores that implement `StorageWithConn` ## Why Use the TCK? - **Consistency**: Ensures all storage implementations behave identically - **Completeness**: Tests all required storage interface methods - **Maintenance**: Reduces test code duplication across storage implementations - **Quality**: Provides comprehensive edge case and error condition testing - **Integration**: Works seamlessly with testcontainers for isolated testing ## Core Concepts ### TCKSuite Interface To use the TCK, you must implement the `TCKSuite` interface: ```go // TCKSuite is the interface that must be implemented by the test suite. // It defines how to create a new store with a container. // The generic parameters are the storage type, the driver type returned by the Conn method, // and the container type used to back the storage. // // IMPORTANT: The container type must exist as a Testcontainers module. // Please refer to the [testcontainers] package for more information. type TCKSuite[T storage.Storage, D any, C testcontainers.Container] interface { // NewStore is a function that returns a new store. // It is called by the [New] function to create a new store. NewStore() func(ctx context.Context, tb testing.TB, ctr C) (T, error) // NewContainer is a function that returns a new container. // It is called by the [New] function to create a new container. NewContainer() func(ctx context.Context, tb testing.TB) (C, error) } ``` **Generic Parameters:** - `T`: Your concrete storage type (e.g., `*mysql.Storage`) - `D`: The driver type returned by `Conn()` method (e.g., `*sql.DB`) - `C`: The testcontainer type (e.g., `*mysql.MySQLContainer`) Please verify that a suitable Testcontainers module exists for your container type. See the [Testcontainers modules catalog](https://testcontainers.com/modules/?language=go) for details. ### Test Execution Modes The TCK supports two execution modes: - **PerTest** (default): Creates a new container and storage instance for each test - **PerSuite**: Creates one container and storage instance for the entire test suite ## Implementation Guide: Example Here's how to implement TCK tests for a new storage backend: ### Step 1: Define Your TCK Implementation ```go // ExampleStorageTCK is the test suite for the Example storage. type ExampleStorageTCK struct{} // NewStore is a function that returns a new Example storage. // It implements the [tck.TCKSuite] interface, allowing the TCK to create a new Example storage // from the container created by the TCK. func (s *ExampleStorageTCK) NewStore() func(ctx context.Context, tb testing.TB, ctr *ExampleContainer) (*Storage, error) { return func(ctx context.Context, tb testing.TB, ctr *example.Container) (*Storage, error) { // Use container APIs to get connection details conn, err := ctr.ConnectionString(ctx) require.NoError(tb, err) store := New(Config{ // Apply the storage-specific configuration ConnectionURI: conn, Reset: true, }) return store, nil } } // NewContainer is a function that returns a new Example container. // It implements the [tck.TCKSuite] interface, allowing the TCK to create a new Example container // for the Example storage. func (s *ExampleStorageTCK) NewContainer() func(ctx context.Context, tb testing.TB) (*example.Container, error) { return func(ctx context.Context, tb testing.TB) (*example.Container, error) { return mustStartExample(tb), nil } } ``` ### Step 2: Implement Container Creation Create a helper function to start your storage backend's container: ```go func mustStartExample(t testing.TB) *example.Container { img := exampleImage if imgFromEnv := os.Getenv(exampleImageEnvVar); imgFromEnv != "" { img = imgFromEnv } ctx := context.Background() c, err := example.Run(ctx, img, example.WithOptionA("valueA"), example.WithOptionB("valueB"), testcontainers.WithWaitStrategy( wait.ForListeningPort("examplePort/tcp"), ), ) testcontainers.CleanupContainer(t, c) require.NoError(t, err) return c } ``` ### Step 3: Create and Run the TCK Test ```go func TestExampleStorageTCK(t *testing.T) { // Create the TCK suite with proper generic type parameters s, err := tck.New[*ExampleStorage, *ExampleDriver, *ExampleContainer]( context.Background(), t, &ExampleStorageTCK{}, tck.PerTest(), // or tck.PerSuite() for suite-level containers ) require.NoError(t, err) // Run all TCK tests suite.Run(t, s) } ``` ## Key Implementation Guidelines ### 1. Generic Type Parameters When calling `tck.New`, specify the correct type parameters: - `T`: Your storage pointer type (e.g., `*Storage`) - `D`: The driver type returned by `Conn()` (or `any` if not applicable) - `C`: The container type returned by `NewContainer()` ### 2. Error Handling Always use `require.NoError(tb, err)` in your factory functions to ensure test failures are properly reported. ### 3. Container Cleanup The TCK handles container cleanup, but ensure your `mustStart*` helpers call `testcontainers.CleanupContainer(t, container)`. For ad‑hoc tests outside the TCK, call `CleanupContainer` to avoid leaving containers running until the test process exits. Although Ryuk will prune them, it’s better to clean up immediately. ### 4. Configuration Configure your storage with appropriate test settings: - Enable `Reset: true` if your storage supports it - Use test-specific database/namespace names - Configure appropriate timeouts and connection limits ### 5. Context Handling Always respect the provided `context.Context` in your factory functions, especially for container startup and storage initialization. ## Testing Different Scenarios ### PerTest Mode (Recommended) Use when you need complete isolation between tests: ```go s, err := tck.New[*Storage, *sql.DB](ctx, t, &ExampleStorageTCK{}, tck.PerTest()) ``` **Pros:** - Complete test isolation - No cross-test contamination - Easier debugging of individual test failures **Cons:** - Slower execution due to container startup overhead - Higher resource usage, although mitigated by Testcontainers' cleanup mechanism ### PerSuite Mode Use when container startup is expensive and tests can share state: ```go s, err := tck.New[*Storage, *sql.DB](ctx, t, &ExampleStorageTCK{}, tck.PerSuite()) ``` **Pros:** - Faster execution - Lower resource usage **Cons:** - Tests may affect each other - Requires careful state management ## Troubleshooting ### Common Issues 1. **Wrong Generic Types**: Ensure type parameters match your actual storage and driver types 2. **Container Startup Failures**: Check wait strategies and ensure proper service readiness 3. **Connection Issues**: Verify connection strings and authentication in your `NewStore()` implementation 4. **Test Isolation**: If tests interfere with each other, consider switching from `PerSuite` to `PerTest` ### Best Practices - Use environment variables for container image versions - Implement proper wait strategies for container readiness - Include cleanup calls even though TCK handles them automatically - Test your TCK implementation with both `PerTest` and `PerSuite` modes - Use meaningful test data that won't conflict across parallel test runs ## Complete Example Template Here's a complete template for implementing TCK tests for a new storage backend: ```go package newstorage import ( "context" "os" "testing" "github.com/gofiber/storage/testhelpers/tck" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go" // Import your specific testcontainer module ) const ( defaultImage = "your-storage-image:latest" imageEnvVar = "TEST_YOUR_STORAGE_IMAGE" ) type YourStorageTCK struct{} func (s *YourStorageTCK) NewStore() func(ctx context.Context, tb testing.TB, ctr *YourContainer) (*Storage, error) { return func(ctx context.Context, tb testing.TB, ctr *YourContainer) (*Storage, error) { // Get connection details from container conn, err := ctr.ConnectionString(ctx) require.NoError(tb, err) // Create and configure your storage store := New(Config{ ConnectionURI: conn, Reset: true, // Add other test-specific configuration }) return store, nil } } func (s *YourStorageTCK) NewContainer() func(ctx context.Context, tb testing.TB) (*YourContainer, error) { return func(ctx context.Context, tb testing.TB) (*YourContainer, error) { return mustStartYourStorage(tb), nil } } func mustStartYourStorage(t testing.TB) *YourContainer { img := defaultImage if imgFromEnv := os.Getenv(imageEnvVar); imgFromEnv != "" { img = imgFromEnv } ctx := context.Background() c, err := yourstorage.Run(ctx, img, // Add your storage-specific configuration testcontainers.WithWaitStrategy( // Add appropriate wait strategies ), ) testcontainers.CleanupContainer(t, c) require.NoError(t, err) return c } func TestYourStorageTCK(t *testing.T) { s, err := tck.New[*Storage, YourDriverType, *YourContainer]( context.Background(), t, &YourStorageTCK{}, tck.PerTest(), ) require.NoError(t, err) suite.Run(t, s) } ``` This template provides a solid foundation for implementing TCK tests for any new storage backend in the Fiber Storage repository. --- ## Valkey ![Release](https://img.shields.io/github/v/tag/gofiber/storage?filter=valkey*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?label=Tests) A fast Valkey Storage that does auto pipelining and supports client side caching. Implementation is based on [valkey-io/valkey](https://github.com/valkey-io/valkey-go). ### Table of Contents - [Signatures](#signatures) - [Installation](#installation) - [Examples](#examples) - [Config](#config) - [Default Config](#default-config) ### Signatures ```go func New(config ...Config) Storage func NewWithContext(ctx context.Context, config ...Config) *Storage func (s *Storage) Get(key string) ([]byte, error) func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) DeleteWithContext(ctx context.Context, key string) error func (s *Storage) Reset() error func (s *Storage) ResetWithContext(ctx context.Context) error func (s *Storage) Close() error func (s *Storage) Conn() valkey.Client ``` ### Installation The valkey driver is tested on the latest two [Go version](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: ```bash go mod init github.com// ``` And then install the valkey implementation: ```bash go get github.com/gofiber/storage/valkey ``` ### Examples Import the storage package. ```go import "github.com/gofiber/storage/valkey" ``` You can use the one of the following options to create a Valkey Storage: ```go // Initialize default config (localhost:6379) store := valkey.New() // Initialize custom config store := valkey.New(valkey.Config{ InitAddress: []string{"localhost:6380"}, Username: "", Password: "", Database: 0, Reset: false, TLSConfig: nil, }) // Initialize using Redis-style URL store := valkey.New(valkey.Config{ URL: "redis://localhost:6379", }) // Initialize Valkey Cluster Client store := valkey.New(valkey.Config{ InitAddress: []string{":6379", ":6380"}, }) // Create a client with support for TLS cer, err := tls.LoadX509KeyPair("./client.crt", "./client.key") if err != nil { log.Println(err) return } tlsCfg := &tls.Config{ MinVersion: tls.VersionTLS12, InsecureSkipVerify: true, Certificates: []tls.Certificate{cer}, } store = valkey.New(valkey.Config{ InitAddress: []string{"localhost:6380"}, Username: "", Password: "", SelectDB: 0, TLSConfig: tlsCfg, }) ``` ### Config ```go type Config struct { // Server username // // Optional. Default is "" Username string // Server password // // Optional. Default is "" Password string // ClientName will execute the `CLIENT SETNAME ClientName` command for each conn. // // Optional. Default is "" ClientName string // URL standard format Redis-style URL. If this is set all other config options, InitAddress, Username, Password, ClientName, and SelectDB have no effect. // // Example: redis://:@localhost:6379/ // Optional. Default is "" URL string // SelectDB to be selected after connecting to the server. // // Optional. Default is 0 SelectDB int // Either a single address or a seed list of host:port addresses, this enables FailoverClient and ClusterClient // // Optional. Default is []string{"127.0.0.1:6379"} InitAddress []string // TLS Config to use. When set TLS will be negotiated. // // Optional. Default is nil TLSConfig *tls.Config // CacheSizeEachConn is valkey client side cache size that bind to each TCP connection to a single valkey instance. // // Optional. The default is DefaultCacheBytes: 128 * (1 << 20) CacheSizeEachConn int // RingScaleEachConn sets the size of the ring buffer in each connection to (2 ^ RingScaleEachConn). // // Optional. The default is RingScaleEachConn, which results into having a ring of size 2^10 for each connection. RingScaleEachConn int // ReadBufferEachConn is the size of the bufio.NewReaderSize for each connection, default to DefaultReadBuffer (0.5 MiB). // // Optional. The default is DefaultReadBuffer: 1 << 19 ReadBufferEachConn int // WriteBufferEachConn is the size of the bufio.NewWriterSize for each connection, default to DefaultWriteBuffer (0.5 MiB). // // Optional. The default is DefaultWriteBuffer: 1 << 19 WriteBufferEachConn int // BlockingPoolSize is the size of the connection pool shared by blocking commands (ex BLPOP, XREAD with BLOCK). // // Optional. The default is DefaultPoolSize: 1000 BlockingPoolSize int // PipelineMultiplex determines how many tcp connections used to pipeline commands to one valkey instance. // // Optional. The default for single and sentinel clients is 2, which means 4 connections (2^2). PipelineMultiplex int // DisableRetry disables retrying read-only commands under network errors // // Optional. The default is False DisableRetry bool // DisableCache falls back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti // // Optional. The default is false DisableCache bool // AlwaysPipelining makes valkey.Client always pipeline valkey commands even if they are not issued concurrently. // // Optional. The default is true AlwaysPipelining bool // Reset clears any existing keys in existing Collection // // Optional. Default is false Reset bool // CacheTTL TTL // // Optional. Default is time.Minute CacheTTL time.Duration } ``` ### Default Config ```go var ConfigDefault = Config{ Username: "", Password: "", ClientName: "", SelectDB: 0, InitAddress: []string{"127.0.0.1:6379"}, TLSConfig: nil, CacheSizeEachConn: valkey.DefaultCacheBytes, RingScaleEachConn: valkey.DefaultRingScale, ReadBufferEachConn: valkey.DefaultReadBuffer, WriteBufferEachConn: valkey.DefaultWriteBuffer, BlockingPoolSize: valkey.DefaultPoolSize, PipelineMultiplex: 2, DisableRetry: false, DisableCache: false, AlwaysPipelining: true, Reset: false, CacheTTL: time.Minute, } ``` --- ## 👋 Welcome(Template) This package provides universal methods to use multiple template engines with the [Fiber web framework](https://github.com/gofiber/fiber) using the new [Views](https://godoc.org/github.com/gofiber/fiber#Views) interface that is available from `> v1.11.1`. Special thanks to @bdtomlin & @arsmn for helping! 9 template engines are supported: - [ace](./ace/README.md) - [amber](./amber/README.md) - [django](./django/README.md) - [handlebars](./handlebars/README.md) - [html](./html/README.md) - [jet](./jet/README.md) - [mustache](./mustache/README.md) - [pug](./pug/README.md) - [slim](./slim/README.md) ### Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get -u github.com/gofiber/fiber/v3 go get -u github.com/gofiber/template/any_template_engine/vX ``` ### Example ```go package main import ( "log" "github.com/gofiber/fiber/v3" // To use a specific template engine, import as shown below: // "github.com/gofiber/template/pug/v3" // "github.com/gofiber/template/mustache/v3" // etc.. // In this example we use the html template engine "github.com/gofiber/template/html/v3" ) func main() { // Create a new engine by passing the template folder // and template extension using .New(dir, ext string) engine := html.New("./views", ".html") // We also support the http.FileSystem interface // See examples below to load templates from embedded files engine := html.NewFileSystem(http.Dir("./views"), ".html") // Reload the templates on each render, good for development engine.Reload(true) // Optional. Default: false // Debug will print each template that is parsed, good for debugging engine.Debug(true) // Optional. Default: false // Layout defines the variable name that is used to yield templates within layouts engine.Layout("embed") // Optional. Default: "embed" // Delims sets the action delimiters to the specified strings engine.Delims("{{", "}}") // Optional. Default: engine delimiters // AddFunc adds a function to the template's global function map. engine.AddFunc("greet", func(name string) string { return "Hello, " + name + "!" }) // After you created your engine, you can pass it to Fiber's Views Engine app := fiber.New(fiber.Config{ Views: engine, }) // To render a template, you can call the ctx.Render function // Render(tmpl string, values interface{}, layout ...string) app.Get("/", func(c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) // Render with layout example app.Get("/layout", func(c fiber.Ctx) error { return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` ### More Examples To view more specific examples, you could visit each engine folder to learn more - [ace](./ace/README.md) - [amber](./amber/README.md) - [django](./django/README.md) - [handlebars](./handlebars/README.md) - [html](./html/README.md) - [jet](./jet/README.md) - [mustache](./mustache/README.md) - [pug](./pug/README.md) - [slim](./slim/README.md) ### embedded Systems We support the `http.FileSystem` interface, so you can use different libraries to load the templates from embedded binaries. #### pkger Read documentation: https://github.com/markbates/pkger ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v3" "github.com/markbates/pkger" ) func main() { engine := html.NewFileSystem(pkger.Dir("/views"), ".html") app := fiber.New(fiber.Config{ Views: engine, }) // run pkger && go build } ``` #### packr Read documentation: https://github.com/gobuffalo/packr ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v3" "github.com/gobuffalo/packr/v2" ) func main() { engine := html.NewFileSystem(packr.New("Templates", "/views"), ".html") app := fiber.New(fiber.Config{ Views: engine, }) // run packr && go build } ``` #### go.rice Read documentation: https://github.com/GeertJohan/go.rice ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v3" "github.com/GeertJohan/go.rice" ) func main() { engine := html.NewFileSystem(rice.MustFindBox("views").HTTPBox(), ".html") app := fiber.New(fiber.Config{ Views: engine, }) // run rice embed-go && go build } ``` #### fileb0x Read documentation: https://github.com/UnnoTed/fileb0x ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v3" // your generated package "github.com///static" ) func main() { engine := html.NewFileSystem(static.HTTP, ".html") app := fiber.New(fiber.Config{ Views: engine, }) // Read the documentation on how to use fileb0x } ``` ### Benchmarks See the benchmarks under https://gofiber.github.io/template/benchmarks #### Simple ![](https://raw.githubusercontent.com/gofiber/template/master/.github/data/Simple-TimeperOperation.png) #### Extended ![](https://raw.githubusercontent.com/gofiber/template/master/.github/data/Extended-TimeperOperation.png) Benchmarks were ran on Apple Macbook M1. Each engine was benchmarked 20 times and the results averaged into a single xlsx file. Mustache was excluded from the extended benchmark --- ## Ace ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=ace*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Ace/badge.svg) Ace is a template engine create by [yossi](https://github.com/yosssi/ace), to see the original syntax documentation please [click here](https://github.com/yosssi/ace/blob/master/documentation/syntax.md) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/ace/v3 ``` ## Basic Example _**./views/index.ace**_ ```html = include ./views/partials/header . h1 {{.Title}} = include ./views/partials/footer . ``` _**./views/partials/header.ace**_ ```html h1 Header ``` _**./views/partials/footer.ace**_ ```html h1 Footer ``` _**./views/layouts/main.ace**_ ```html = doctype html html head title Main body {{embed}} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/ace/v3" ) func main() { // Create a new engine engine := ace.New("./views", ".ace") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := html.NewFileSystem(http.Dir("./views", ".ace")) // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Amber ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=amber*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Amber/badge.svg) Amber is a template engine create by [eknkc](https://github.com/eknkc/amber), to see the original syntax documentation please [click here](https://github.com/eknkc/amber#tags) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/amber/v3 ``` ## Basic Example _**./views/index.amber**_ ```html import ./views/partials/header h1 #{Title} import ./views/partials/footer ``` _**./views/partials/header.amber**_ ```html h1 Header ``` _**./views/partials/footer.amber**_ ```html h1 Footer ``` _**./views/layouts/main.amber**_ ```html doctype html html head title Main body #{embed()} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/amber/v3" ) func main() { // Create a new engine engine := amber.New("./views", ".amber") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := html.NewFileSystem(http.Dir("./views", ".amber")) // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Django ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=django*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Django/badge.svg) Django is a template engine create by [flosch](https://github.com/flosch/pongo2), to see the original syntax documentation please [click here](https://docs.djangoproject.com/en/dev/topics/templates/) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/django/v4 ``` ## Basic Example _**./views/index.django**_ ```html {% include "partials/header.django" %}

{{ Title }}

{% include "partials/footer.django" %} ``` _**./views/partials/header.django**_ ```html

Header

``` _**./views/partials/footer.django**_ ```html

Footer

``` _**./views/layouts/main.django**_ ```html Main {{embed}} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/django/v4" ) func main() { // Create a new engine engine := django.New("./views", ".django") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := html.NewFileSystem(http.Dir("./views", ".django")) // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` ### Using embedded file system (1.16+ only) When using the `// go:embed` directive, resolution of inherited templates using django's `{% extend '' %}` keyword fails when instantiating the template engine with `django.NewFileSystem()`. In that case, use the `django.NewPathForwardingFileSystem()` function to instantiate the template engine. This function provides the proper configuration for resolving inherited templates. Assume you have the following files: - [views/ancenstor.django](https://github.com/gofiber/template/blob/master/django/views/ancestor.django) - [views/descendant.djando](https://github.com/gofiber/template/blob/master/django/views/descendant.django) then ```go package main import ( "log" "embed" "net/http" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/django/v4" ) //go:embed views var viewsAsssets embed.FS func main() { // Create a new engine engine := django.NewPathForwardingFileSystem(http.FS(viewsAsssets), "/views", ".django") // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render descendant return c.Render("descendant", fiber.Map{ "greeting": "World", }) }) log.Fatal(app.Listen(":3000")) } ``` ### Register and use custom functions ```go // My custom function func Nl2brHtml(value interface{}) string { if str, ok := value.(string); ok { return strings.Replace(str, "\n", "
", -1) } return "" } // Create a new engine engine := django.New("./views", ".django") // register functions engine.AddFunc("nl2br", Nl2brHtml) // Pass the engine to the Views app := fiber.New(fiber.Config{Views: engine}) ``` _**in the handler**_ ```go c.Render("index", fiber.Map{ "Fiber": "Hello, World!\n\nGreetings from Fiber Team", }) ``` _**./views/index.django**_ ```html {{ nl2br(Fiber) }} ``` **Output:** ```html Hello, World!

Greetings from Fiber Team ``` ### Important Information on Template Data Binding When working with Pongo2 and this template engine, it's crucial to understand the specific rules for data binding. Only keys that match the following regular expression are supported: `^[a-zA-Z0-9_]+$`. This means that keys with special characters or punctuation, such as `my-key` or `my.key`, are not compatible and will not be bound to the template. This is a restriction imposed by the underlying Pongo2 template engine. Please ensure your keys adhere to these rules to avoid any binding issues. If you need to access a value in the template that doesn't adhere to the key naming restrictions imposed by the Pongo2 template engine, you can bind the value to a new field when calling `fiber.Render`. Here's an example: ```go c.Render("index", fiber.Map{ "Fiber": "Hello, World!\n\nGreetings from Fiber Team", "MyKey": c.Locals("my-key"), }) ``` ### AutoEscape is enabled by default When you create a new instance of the `Engine`, the auto-escape is **enabled by default**. This setting automatically escapes output, providing a critical security measure against Cross-Site Scripting (XSS) attacks. ### Disabling Auto-Escape Auto-escaping can be disabled if necessary, using the `SetAutoEscape` method: ```go engine := django.New("./views", ".django") engine.SetAutoEscape(false) ``` ### Setting AutoEscape using Django built-in template tags - Explicitly turning off autoescaping for a section: ```django {% autoescape off %} {{ "" }} {% endautoescape %} ``` - Turning autoescaping back on for a section: ```django {% autoescape on %} {{ "" }} {% endautoescape %} ``` - It can also be done on a per variable basis using the *safe* built-in: ```django

{{ someSafeVar | safe }}

{{ " ``` ```js // Javascript var cat = {"Name":"Sam", "Age":12} ``` --- #### Safe Strings and HTML Comments The `html/template` package will remove any comments from a template by default. This can cause issues when comments are necessary such as detecting internet explorer. ```html ``` We can use the Custom Functions method (Globally) to create a function that returns html preserving comments. Define a function `htmlSafe` in the FuncMap of the template. ```go testTemplate, err = template.New("hello.gohtml").Funcs(template.FuncMap{ "htmlSafe": func(html string) template.HTML { return template.HTML(html) }, }).ParseFiles("hello.gohtml") ``` This function takes a string and produces the unaltered HTML code. This function can be used in a template like so to preserve the comments `` : ```go {{htmlSafe "" }} ``` --- ## Template Variables #### The dot character (.) A template variable can be a boolean, string, character, integer, floating-point, imaginary, or complex constant in Go syntax. Data passed to the template can be accessed using dot `{{ . }}`. If the data is a complex type then it’s fields can be accessed using the dot with the field name `{{ .FieldName }}`. Dots can be chained together if the data contains multiple complex structures. `{{ .Struct.StructTwo.Field }}` --- #### Variables in Templates Data passed to the template can be saved in a variable and used throughout the template. `{{$number := .}}` We use the `$number` to create a variable then initialize it with the value passed to the template. To use the variable we call it in the template with `{{$number}}`. ```go {{$number := .}}

It is day number {{$number}} of the month

``` ```go var tpl *template.Template tpl = template.Must(template.ParseFiles("templateName")) err := tpl.ExecuteTemplate(os.Stdout, "templateName", 23) ``` In this example we pass 23 to the template and stored in the `$number` variable which can be used anywhere in the template --- ## Template Actions #### If/Else Statements Go templates support if/else statements like many programming languages. We can use the if statement to check for values, if it doesn’t exist we can use an else value. The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero. ```html

Hello, {{if .Name}} {{.Name}} {{else}} Anonymous {{end}}!

``` If .Name exists then `Hello, Name` will be printed (replaced with the name value) otherwise it will print `Hello, Anonymous`. Templates also provide the else if statment `{{else if .Name2 }}` which can be used to evaluate other options after an if. --- #### Removing Whitespace Adding different values to a template can add various amounts of whitespace. We can either change our template to better handle it, by ignoring or minimizing effects, or we can use the minus sign `-` within out template. `

Hello, {{if .Name}} {{.Name}} {{- else}} Anonymous {{- end}}!

` Here we are telling the template to remove all spaces between the `Name` variable and whatever comes after it. We are doing the same with the end keyword. This allows us to have whitespace within the template for easier reading but remove it in production. --- #### Range Blocks Go templates have a `range` keyword to iterate over all objects in a structure. Suppose we had the Go structures: ```go type Item struct { Name string Price int } type ViewData struct { Name string Items []Item } ``` We have an Item, with a name and price, then a ViewData which is the structure sent to the template. Consider the template containing the following: ```html {{range .Items}}

{{.Name}}

${{.Price}}
{{end}} ``` For each Item in the range of Items (in the ViewData structure) get the Name and Price of that item and create html for each Item automatically. Within a range each Item becomes the `{{.}}` and the item properties therefore become `{{.Name}}` or `{{.Price}}` in this example. --- ## Template Functions The template package provides a list of predefined global functions. Below are some of the most used. --- #### Indexing structures in Templates If the data passed to the template is a map, slice, or array it can be indexed from the template. We use `{{index x number}}` where index is the keyword, x is the data and number is a integer for the index value. If we had `{{index names 2}}` it is equivalent to `names[2]`. We can add more integers to index deeper into data. `{{index names 2 3 4}}` is equivalent to `names[2][3][4]`. ```html

{{index .FavNums 2 }}

``` ```go type person struct { Name string FavNums []int } func main() { tpl := template.Must(template.ParseGlob("*.gohtml")) tpl.Execute(os.Stdout, &person{"Curtis", []int{7, 11, 94}}) } ``` This code example passes a person structure and gets the 3rd favourite number from the FavNums slice. --- #### The `and` Function The and function returns the boolean AND of its arguments by returning the first empty argument or the last argument. `and x y` behaves logically as `if x then y else x` . Consider the following go code ```go type User struct { Admin bool } type ViewData struct { *User } ``` Pass a ViewData with a User that has Admin set true to the following template ```go {{if and .User .User.Admin}} You are an admin user! {{else}} Access denied! {{end}} ``` The result will be `You are an admin user!`. However if the ViewData did not include a \*User object or Admin was set as false then the result will be `Access denied!`. --- #### The `or` Function The or function operates similarly to the and function however will stop at the first true. `or x y` is equivalent to `if x then x else y` so y will never be evaluated if x is not empty. --- #### The `not` Function The not function returns the boolean negation of the argument. ```go {{ if not .Authenticated}} Access Denied! {{ end }} ``` --- ## Template Comparison Functions #### Comparisons The `html/template` package provides a variety of functions to do comparisons between operators. The operators may only be basic types or named basic types such as `type Temp float32` Remember that template functions take the form `{{ function arg1 arg2 }}`. - `eq` Returns the result of `arg1 == arg2` - `ne` Returns the result of `arg1 != arg2` - `lt` Returns the result of `arg1 < arg2` - `le` Returns the result of `arg1 <= arg2` - `gt` Returns the result of `arg1 > arg2` - `ge` Returns the result of `arg1 >= arg2` Of special note `eq` can be used with two or more arguments by comparing all arguments to the first. `{{ eq arg1 arg2 arg3 arg4}}` will result in the following logical expression: `arg1==arg2 || arg1==arg3 || arg1==arg4` --- ## Nested Templates and Layouts #### Nesting Templates Nested templates can be used for parts of code frequently used across templates, a footer or header for example. Rather than updating each template separately we can use a nested template that all other templates can use. You can define a template as follows: ```go {{define "footer"}}

Here is the footer

{{end}} ``` A template named “footer” is defined which can be used in other templates like so to add the footer template content into the other template: ```go {{template "footer"}} ``` --- #### Passing Variables between Templates The `template` action used to include nested templates also allows a second parameter to pass data to the nested template. ```html // Define a nested template called header {{define "header"}}

{{.}}

{{end}} // Call template and pass a name parameter {{range .Items}}
{{template "header" .Name}} ${{.Price}}
{{end}} ``` We use the same range to loop through Items as before but we pass the name to the header template each time in this simple example. --- #### Creating Layouts Glob patterns specify sets of filenames with wildcard characters. The `template.ParseGlob(pattern string)` function will parse all templates that match the string pattern. `template.ParseFiles(files...)` can also be used with a list of file names. The templates are named by default based on the base names of the argument files. This mean `views/layouts/hello.gohtml` will have the name `hello.gohtml` . If the template has a `{{define “templateName”}}` within it then that name will be usable. A specific template can be executed using `t.ExecuteTemplate(w, "templateName", nil)` . `t` is an object of type Template, `w` is type io.Writer such as an `http.ResponseWriter`, Then there is the name of the template to execute, and finally passing any data to the template, in this case a nil value. Example main.go file ```go // Omitted imports & package var LayoutDir string = "views/layouts" var bootstrap *template.Template func main() { var err error bootstrap, err = template.ParseGlob(LayoutDir + "/*.gohtml") if err != nil { panic(err) } http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } func handler(w http.ResponseWriter, r *http.Request) { bootstrap.ExecuteTemplate(w, "bootstrap", nil) } ``` All `.gohtml` files are parsed in main. When route `/` is reached the template defined as `bootstrap` is executed using the handler function. Example views/layouts/bootstrap.gohtml file ```html {{define "bootstrap"}} Go Templates

Filler header

Filler paragraph

{{end}} ``` ## Templates Calling Functions #### Function Variables (calling struct methods) We can use templates to call the methods of objects in the template to return data. Consider the User struct with the following method. ```go type User struct { ID int Email string } func (u User) HasPermission(feature string) bool { if feature == "feature-a" { return true } else { return false } } ``` When a type User has been passed to the template we can then call this method from the template. ```html {{if .User.HasPermission "feature-a"}}

Feature A

Some other stuff here...

{{else}}

Feature A

To enable Feature A please upgrade your plan

{{end}} ``` The template checks if the User HasPermission for the feature and renders depending on the result. --- #### Function Variables (call) If the Method HasPermission has to change at times then the Function Variables (Methods) implementation may not fit the design. Instead a `HasPermission func(string) bool` attribute can be added on the `User` type. This can then have a function assigned to it at creation. ```go // Structs type ViewData struct { User User } type User struct { ID int Email string HasPermission func(string) bool } // Example of creating a ViewData vd := ViewData{ User: User{ ID: 1, Email: "curtis.vermeeren@gmail.com", // Create the HasPermission function HasPermission: func(feature string) bool { if feature == "feature-b" { return true } return false }, }, } // Executing the ViewData with the template err := testTemplate.Execute(w, vd) ``` We need to tell the Go template that we want to call this function so we must change the template from the Function Variables (Methods) implementation to do this. We use the `call` keyword supplied by the go `html/template` package. Changing the previous template to use `call` results in: ```html {{if (call .User.HasPermission "feature-b")}}

Feature B

Some other stuff here...

{{else}}

Feature B

To enable Feature B please upgrade your plan

{{end}} ``` --- #### Custom Functions Another way to call functions is to create custom functions with `template.FuncMap` . This method creates global methods that can be used throughout the entire application. FuncMap has type `map[string]interface{}` mapping a string, the function name, to a function. The mapped functions must have either a single return value, or two return values where the second has type error. ```go // Creating a template with function hasPermission testTemplate, err = template.New("hello.gohtml").Funcs(template.FuncMap{ "hasPermission": func(user User, feature string) bool { if user.ID == 1 && feature == "feature-a" { return true } return false }, }).ParseFiles("hello.gohtml") ``` Here the function to check if a user has permission for a feature is mapped to the string `"hasPermission"` and stored in the FuncMap. Note that the custom functions must be created before calling `ParseFiles()` The function could be executed in the template as follows: ```go {{ if hasPermission .User "feature-a" }} ``` The `.User` and string `"feature-a"` are both passed to `hasPermission` as arguments. --- #### Custom Functions (Globally) The previous two methods of custom functions rely on `.User` being passed to the template. This works in many cases but in a large application passing too many objects to a template can become difficult to maintain across many templates. We can change the implementation of the custom function to work without the .User being passed. Using a similar feature example as the other 2 sections first you would have to create a default `hasPermission` function and define it in the template’s function map. ```go testTemplate, err = template.New("hello.gohtml").Funcs(template.FuncMap{ "hasPermission": func(feature string) bool { return false }, }).ParseFiles("hello.gohtml") ``` This function could be placed in `main()` or somewhere that ensures the default `hasPermission` is created in the `hello.gohtml` function map. The default function just returns false but it defines the function and implementation that doesn’t require `User` . Next a closure could be used to redefine the `hasPermission` function. It would use the `User` data available when it is created in a handler rather than having `User` data passed to it. Within the handler for the template you can redefine any functions to use the information available. ```go func handler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") user := User{ ID: 1, Email: "Curtis.vermeeren@gmail.com", } vd := ViewData{} err := testTemplate.Funcs(template.FuncMap{ "hasPermission": func(feature string) bool { if user.ID == 1 && feature == "feature-a" { return true } return false }, }).Execute(w, vd) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } ``` In this handler a `User` is created with ID and Email, Then a `ViewData` is created without passing the user to it. The `hasPermission` function is redefined using `user.ID` which is available when the function is created. `{{if hasPermission "feature-a"}}` can be used in a template without having to pass a `User` to the template as the User object in the handler is used instead. --- --- ## Jet ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=jet*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Jet/badge.svg) Jet is a template engine create by [cloudykit](https://github.com/CloudyKit/jet), to see the original syntax documentation please [click here](https://github.com/CloudyKit/jet/wiki/3.-Jet-template-syntax) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/jet/v3 ``` ## Basic Example _**./views/index.jet**_ ```html {{include "partials/header"}}

{{ Title }}

{{include "partials/footer"}} ``` _**./views/partials/header.jet**_ ```html

Header

``` _**./views/partials/footer.jet**_ ```html

Footer

``` _**./views/layouts/main.jet**_ ```html Title {{ embed() }} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/jet/v3" ) func main() { // Create a new engine engine := jet.New("./views", ".jet") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := jet.NewFileSystem(http.Dir("./views"), ".jet") // Pass the engine to the views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Mustache ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=mustache*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Mustache/badge.svg) Mustache is a template engine created by [hoisie/cbroglie](https://github.com/cbroglie/mustache), to see the original syntax documentation please [click here](https://mustache.github.io/mustache.5.html) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/mustache/v4 ``` ## Basic Example _**./views/index.mustache**_ ```html {{> views/partials/header }}

{{Title}}

{{> views/partials/footer }} ``` _**./views/partials/header.mustache**_ ```html

Header

``` _**./views/partials/footer.mustache**_ ```html

Footer

``` _**./views/layouts/main.mustache**_ ```html Main {{{embed}}} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/mustache/v3" ) func main() { // Create a new engine engine := mustache.New("./views", ".mustache") // Or from an embedded system // Note that with an embedded system the partials included from template files must be // specified relative to the filesystem's root, not the current working directory // engine := mustache.NewFileSystem(http.Dir("./views", ".mustache"), ".mustache") // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Pug ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=pug*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Pug/badge.svg) Pug is a template engine create by [joker](https://github.com/Joker/jade), to see the original syntax documentation please [click here](https://pugjs.org/language/tags.html) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/pug/v3 ``` ## Basic Example _**./views/index.pug**_ ```html include partials/header.pug h1 #{.Title} include partials/footer.pug ``` _**./views/partials/header.pug**_ ```html h2 Header ``` _**./views/partials/footer.pug**_ ```html h2 Footer ``` _**./views/layouts/main.pug**_ ```html doctype html html head title Main include ../partials/meta.pug body | {{embed}} ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/pug/v3" // "net/http" // embedded system ) func main() { // Create a new engine engine := pug.New("./views", ".pug") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := pug.NewFileSystem(http.Dir("./views"), ".pug") // Pass the engine to the views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ``` --- ## Slim ![Release](https://img.shields.io/github/v/tag/gofiber/template?filter=slim*) [![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) ![Test](https://github.com/gofiber/template/workflows/Tests%20Slim/badge.svg) Slim is a template engine created by [mattn](https://github.com/mattn/go-slim), to see the original syntax documentation please [click here](https://rubydoc.info/gems/slim/frames) ## Installation Go version support: We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information. ``` go get github.com/gofiber/template/slim/v3 ``` ## Basic Example _**./views/index.slim**_ ```html == render("partials/header.slim") h1 = Title == render("partials/footer.slim") ``` _**./views/partials/header.slim**_ ```html h2 = Header ``` _**./views/partials/footer.slim**_ ```html h2 = Footer ``` _**./views/layouts/main.slim**_ ```html doctype html html head title Main include ../partials/meta.slim body == embed ``` ```go package main import ( "log" "github.com/gofiber/fiber/v3" "github.com/gofiber/template/slim/v3" // "net/http" // embedded system ) func main() { // Create a new engine engine := slim.New("./views", ".slim") // Or from an embedded system // See github.com/gofiber/embed for examples // engine := slim.NewFileSystem(http.Dir("./views", ".slim")) // Pass the engine to the Views app := fiber.New(fiber.Config{ Views: engine, }) app.Get("/", func(c fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ "Title": "Hello, World!", }) }) app.Get("/layout", func(c fiber.Ctx) error { // Render index within layouts/main return c.Render("index", fiber.Map{ "Title": "Hello, World!", }, "layouts/main") }) log.Fatal(app.Listen(":3000")) } ```