DEV Community

Cover image for Middleware in Go
OdaloV
OdaloV

Posted on

Middleware in Go

If you've built any web application in Go, you've probably heard about middlewares. But what exactly are they?

A middleware is just a function that sits between your HTTP server and your route handlers. It can inspect, modify, or even stop a request before it reaches your main application logic.

Think of it as a pipeline.Every request passes through each middleware in order before hitting your handler, and the response travels back through them on the way out.

Example

A middleware accepts a handler and returns a new one:

func MyMiddleware(next HandlerFunc) HandlerFunc {
    return func(c Context) error {
        // runs before your handler
        err := c.Next(next)
        // runs after your handler
        return err
    }
}
Enter fullscreen mode Exit fullscreen mode

c.Next()

When you register multiple middlewares, your framework doesn't run them independently. It builds a chain. Each middleware wraps the next one, like nested functions. c.Next() is the call that says ,pass control to whatever comes next in that chain.

Without it, the chain stops dead. Your route handler never runs, and the client gets no response.

This also means you can intentionally skip it to reject a request early:

func RequireAuth(next HandlerFunc) HandlerFunc {
    return func(c Context) error {
        if c.Request().Header.Get("Authorization") == "" {
            return c.String(http.StatusUnauthorized, "unauthorized")
        }
        return c.Next(next) // only reach here if auth passes
    }
}
Enter fullscreen mode Exit fullscreen mode

Registering middleware

Most frameworks let you attach middleware at three levels.

Global middleware runs on every single request your app receives ,logger and panic recovery belong here:

app.Use(Logger)
app.Use(Recover)
Enter fullscreen mode Exit fullscreen mode

Group middleware applies only to a subset of routes. This is the right place for auth ,you probably don't want to guard your /health endpoint the same way you guard /api/users:

api := app.Group("/api")
api.Use(RequireAuth)
Enter fullscreen mode Exit fullscreen mode

Route-level middleware is for one-off logic that doesn't belong anywhere else:

app.GET("/admin", adminHandler, RequireAdmin)
Enter fullscreen mode Exit fullscreen mode

If a middleware needs to run everywhere, make it global. If it only makes sense for a set of routes, scope it to a group. Route-level is a last resort.

Top comments (0)