fiber/docs/middleware/timeout.md
Juan Calderon-Perez 40e1e76a1c
📒 docs: Improve Timeout middleware documentation (#3675)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-11 12:28:18 +02:00

4.0 KiB

id
id
timeout

Timeout

The timeout middleware limits how long a handler can take to complete. When the deadline is exceeded, Fiber responds with 408 Request Timeout. The middleware uses context.WithTimeout and makes the context available through c.Context() so that your code can listen for cancellation.

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

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 request's context is canceled:

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"))
}

Test it with curl:

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 the middleware. 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 custom error

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

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"))
}