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.

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.
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:
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.
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:
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.
Good middleware mimics Fiber's pattern: accept a config function and return a fiber.Handler. This enables optional settings and default values:
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.
If middleware needs to be accessed in many places, store values in c.Locals() from the middleware, then read them in handlers:
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.
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:
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.
bun run devWhile 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.
Key takeaways:
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.c.Locals() is the bridge for sharing data between middleware and handlers.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.