Learning Fiber - Middleware and Lifecycle Hooks
Episode 6 of 23

Learning Fiber - Middleware and Lifecycle Hooks

This episode covers middleware in Fiber v3: writing your own middleware with app.Use, understanding sequential execution and c.Next(), built-in middleware like Logger and Recover, and the lifecycle hooks OnListen, OnShutdown, OnPreShutdown, and OnPostShutdown.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Middleware is the layer that welcomes every request before it reaches a handler. Episode 6 dissects how middleware works in Fiber v3: writing your own with app.Use, using built-in middleware, and running code at specific points in the application lifecycle via hooks.

The key concept is simple: one request travels through a pipeline. Each middleware can process, modify, or stop the request, then call c.Next() to pass it along. Understanding this pipeline will make mastering all of Fiber's features easier.

How Middleware Works

Pipeline and c.Next()

Middleware is simply func(c fiber.Ctx) error. The one registered first executes first. When a middleware calls c.Next(), execution moves to the next handler in the pipeline. Code after c.Next() runs when the request returns from the next handler:

Middleware dengan c.Next()
app.Use(func(c fiber.Ctx) error {
    start := time.Now()
    err := c.Next()
    elapsed := time.Since(start)
    log.Printf("path=%s duration=%s", c.Path(), elapsed)
    return err
})

This middleware measures duration by wrapping c.Next(). If a middleware doesn't call c.Next() and returns a response directly, the pipeline stops there — the request never reaches the handler.

Built-in Middleware: Logger and Recover

Fiber provides many ready-made middleware. The two most used: Logger to record every request, and Recover to catch panics so the server doesn't die:

Logger dan Recover
import (
    "github.com/gofiber/fiber/v3"
    "github.com/gofiber/fiber/v3/middleware/logger"
    "github.com/gofiber/fiber/v3/middleware/recover"
)
 
app.Use(logger.New())
app.Use(recover.New())
 
app.Get("/panic", func(c fiber.Ctx) error {
    panic("ups!")
})

logger.New() prints a log line for every request complete with status and duration. recover.New() catches panics in handlers — like panic("ups!") — returns status 500, and calls the OnRecover hook to send error notifications.

Writing Your Own Middleware

Middleware with Configuration

Good middleware mimics Fiber's pattern: accept a config function and return a fiber.Handler. This enables optional settings and default values:

Middleware kustom dengan config
type CacheConfig struct {
    Duration time.Duration
    Key      func(c fiber.Ctx) string
}
 
func Cache(config ...CacheConfig) fiber.Handler {
    cfg := CacheConfig{Duration: time.Minute}
    if len(config) > 0 {
        cfg = config[0]
    }
    return func(c fiber.Ctx) error {
        key := cfg.Key(c)
        if cached, ok := getFromCache(key); ok {
            return c.SendString(cached)
        }
        if err := c.Next(); err != nil {
            return err
        }
        setCache(key, c.Response().Body(), cfg.Duration)
        return nil
    }
}

The Cache function accepts optional config and returns a handler. The variadic parameter config ...CacheConfig preserves compatibility — Cache() without arguments still works with default values.

Middleware with Global Parameters

If middleware needs to be accessed in many places, store values in c.Locals() from the middleware, then read them in handlers:

Berbagi nilai lewat Locals
app.Use(func(c fiber.Ctx) error {
    c.Locals("request_id", uuid.NewString())
    return c.Next()
})
 
app.Get("/users/:id", func(c fiber.Ctx) error {
    rid := c.Locals("request_id").(string)
    return c.JSON(fiber.Map{"request_id": rid})
})

c.Locals("request_id", ...) stores a value in the request context, and c.Locals("request_id") reads it back in the handler. This pattern is widely used for request IDs, authenticated users, and other transient data.

Lifecycle Hooks in v3

Running Code When the Server Starts and Stops

Fiber v3 offers hooks for the important moments in an app's life: when the server starts listening, when shutdown begins, and after shutdown completes:

Lifecycle hooks
app.OnListen(func(addr fiber.ListenOnListenData) error {
    log.Printf("server listen di %s", addr.Addr)
    return nil
})
 
app.OnShutdown(func() {
    log.Println("shutdown dimulai, menutup koneksi")
})
 
app.OnPreShutdown(func() {
    log.Println("sebelum shutdown, migrasi dibatalkan")
})
 
app.OnPostShutdown(func() {
    log.Println("semua selesai, bersih-bersih")
})

OnListen is called when the server starts accepting connections. OnShutdown and OnPreShutdown run during graceful shutdown, and OnPostShutdown after all listeners have stopped. These hooks are useful for closing database connections and canceling background jobs.

Testing

Melihat middleware bekerja
bun run dev

While bun run dev is running, hit a few endpoints and look at the terminal output. logger.New() prints a log line per request, and the server shows the server listen di ... message from the OnListen hook. Press Ctrl+C to trigger shutdown and observe the order of messages from the shutdown hooks.

Closing

Key takeaways:

  • Middleware is func(c fiber.Ctx) error executed sequentially in a pipeline.
  • c.Next() passes the request to the next handler; code after it runs when the request returns.
  • logger.New() and recover.New() are the most commonly used built-in middleware.
  • Custom middleware follows the variadic config pattern to preserve compatibility.
  • c.Locals() is the bridge for sharing data between middleware and handlers.
  • The OnListen, OnShutdown, OnPreShutdown, and OnPostShutdown hooks manage the server lifecycle.

In the next episode, episode 7, we discuss advanced routing and handler parameters — HTTP methods, multiple handlers per route, variadic handlers, and how many handlers can be registered per route.

Learning Fiber - Middleware and Lifecycle Hooks | Learn Fiber