This episode covers hooks and the event-driven pattern in Fiber v3: the OnListen and OnShutdown lifecycle hooks, hooks when routes and groups are registered, custom events via On and Emit, and using hooks for instrumentation and observability.

In episode 6 you saw the OnListen and OnShutdown hooks. Episode 15 expands this topic into an event-driven pattern: Fiber v3 registers hooks not only for the server lifecycle, but also for events inside the application — such as when a route is registered — plus custom events you can define yourself.
The event-driven pattern separates "what happens" from "who responds". This is very useful for logging, metrics, audit, and integrations — monitoring code no longer gets mixed into every handler.
Lifecycle hooks give you entry points at the start and end of the server's life. They can be registered directly or chained:
app.Hooks().OnListen(func(addr fiber.ListenOnListenData) error {
log.Printf("listening on %s", addr.Addr)
return nil
})
app.Hooks().OnShutdown(func() {
closeDB()
cancelBackgroundJobs()
})OnListen is called when the server is ready to accept connections; OnShutdown when graceful shutdown begins. The full family: OnListen, OnShutdown, OnPreShutdown, OnPostShutdown, OnMount, OnUnmount, and OnFork. Closing resources in hooks is cleaner than putting them in main.
Fiber v3 fires hooks every time a route, name, or group is registered. This opens the door to automatic tooling:
app.Hooks().OnRoute(func(r fiber.Route) error {
log.Printf("route terdaftar: %s %s", r.Method, r.Path)
return nil
})
app.Hooks().OnGroup(func(g *fiber.Group) error {
log.Printf("group dibuat: prefix=%s", g.Prefix)
return nil
})OnRoute is called every time a new route is registered, carrying a fiber.Route object with method and path. OnGroup is called when a group is created. With these hooks, an endpoint list can be generated automatically for documentation without writing it by hand.
Besides built-in hooks, Fiber v3 supports custom events through On and Emit:
app.Hooks().On("user:created", func(data any) {
metrics.Inc("user_created")
notify("user", data)
})
app.Post("/users", func(c fiber.Ctx) error {
var user User
if err := c.Bind().JSON(&user); err != nil {
return err
}
createUser(user)
app.Hooks().Emit("user:created", user)
return c.Status(fiber.StatusCreated).JSON(user)
})app.Hooks().On("user:created", fn) registers a listener; app.Hooks().Emit("user:created", user) sends an event complete with data. Handlers just call Emit — they don't need to know who's listening. Adding new features (email, webhook, audit) only requires adding a listener, without touching the handler.
Custom events will grow over time. Set a naming pattern early so they're easy to track:
app.Hooks().On("order:placed", handleOrderPlaced)
app.Hooks().On("order:payment-failed", handlePaymentFailed)
app.Hooks().On("order:fulfilled", handleOrderFulfilled)The domain:action pattern — order:placed, payment-failed, fulfilled — makes events easy to find and map. Naming consistency matters when you have dozens of events and listeners spread across many files.
The combination of built-in hooks and custom events forms an observability layer without polluting business code:
app.Hooks().OnListen(func(d fiber.ListenOnListenData) error {
metrics.Gauge("uptime", 0)
return nil
})
app.Hooks().OnRoute(func(r fiber.Route) error {
auditLog("route_registered", map[string]string{
"method": r.Method, "path": r.Path,
})
return nil
})A single registration point records every route for audit; a single OnListen listener initializes metrics. The result: logs and metrics that are consistent, centralized, and easy to remove when no longer needed.
curl -X POST -H "Content-Type: application/json" \
--data '{"name":"Budi"}' http://localhost:3000/usersWhile the server runs, look at the terminal: the OnListen and OnRoute hooks print logs at startup. When the POST request above is processed, the user:created event triggers the metrics and notification listeners — all recorded without extra code in the main handler.
Key takeaways:
OnListen, OnShutdown, OnPreShutdown, OnPostShutdown, OnMount, OnUnmount, OnFork.OnRoute and OnGroup fire events when routes/groups are registered — useful for tooling.app.Hooks().On(name, fn) and app.Hooks().Emit(name, data).Emit; listeners handle the side effects.domain:action naming pattern keeps events organized.In the next episode, episode 16, we discuss JWT and security — the jwt middleware in Fiber, token validation, secret storage, and API authentication security practices.