fiber/docs/middleware/timeout.md
RW 05f8ac0255
🔥 feat: Add config for Timeout middleware (#3604)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-20 15:22:08 +02:00

4.0 KiB

id
id
timeout

Timeout

There exist two distinct implementations of timeout middleware Fiber.

New

As a fiber.Handler wrapper, it creates a context with context.WithTimeout which is then used with c.Context().

If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized ErrorHandler.

It does not cancel long running executions. Underlying executions must handle timeout by using context.Context parameter.

Signatures

func New(handler fiber.Handler, config ...timeout.Config) fiber.Handler

Examples

Import the middleware package that is part of the Fiber web framework

import (
    "github.com/gofiber/fiber/v3"
    "github.com/gofiber/fiber/v3/middleware/timeout"
)

After you initiate your Fiber app, you can use the following possibilities:

func main() {
    app := fiber.New()
    h := func(c fiber.Ctx) error {
        sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms")
        if err := sleepWithContext(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}))
    log.Fatal(app.Listen(":3000"))
}

func sleepWithContext(ctx context.Context, d time.Duration) error {
    timer := time.NewTimer(d)

    select {
    case <-ctx.Done():
        if !timer.Stop() {
            <-timer.C
        }
        return context.DeadlineExceeded
    case <-timer.C:
    }
    return nil
}

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

Test http 200 with curl:

curl --location -I --request GET 'http://localhost:3000/foo/1000' 

Test http 408 with curl:

curl --location -I --request GET 'http://localhost:3000/foo/3000' 

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