Skip to main content

Middleware

Developer Intermediate

Middleware runs before your controller handlers. For who is logged in and what they may do, EZ-Console layers authentication and RBAC on the API router; this page is the canonical place for route registration patterns. Conceptual background (JWT, OAuth, LDAP, MFA) lives in Authentication & Authorization.

Default API authentication​

When controllers register routes on the stock API group, the framework already applies WithAuthentication: every route expects a valid login (for example a bearer token) unless you explicitly opt out.

You do not need to call middleware.RequireAuth() on each handler for that default behaviorβ€”it is already part of the group’s middleware chain.

Public routes (WithoutAuthentication)​

Because WithAuthentication is registered on the API group by default, it runs before most handlers. For login-free endpoints you must wrap the route group with middleware.WithoutAuthentication, which inserts WithoutAuthenticationMiddleware ahead of the default authentication middleware so those routes are evaluated first.

site := middleware.WithoutAuthentication(router.Group("/site"))
site.GET("", c.GetSite)

Use this only for endpoints that are intentionally public (health, site metadata, OAuth callbacks, etc.).

Permission checks (RequirePermission)​

After authentication, some routes should be callable by any logged-in user, while others need specific RBAC permissions. Add middleware.RequirePermission (or the variants below) only where you need authorization beyond β€œlogged in”.

chat := router.Group("/chat")
chat.GET("/sessions", c.ListChatSessions) // any authenticated user
chat.POST("/sessions", middleware.RequirePermission("ai:chat:create"), c.CreateChatSession) // requires ai:chat:create

Additional helpers (same package):

  • middleware.RequireAnyPermission(...) β€” grant if the user has any of the listed permissions
  • middleware.RequireAllPermissions(...) β€” grant only if the user has all of them

Permission naming and RBAC model: Authentication & Authorization.

Other common middleware​

Examples below assume github.com/sven-victor/ez-console/pkg/middleware (names may vary slightly by version; prefer your module’s API).

CORS​

router.Use(middleware.CORS())

Logging​

router.Use(middleware.Logging())

Recovery (panic handler)​

router.Use(middleware.Recovery())

Global middleware (WithEngineOptions)​

For a normal EZ-Console binary you do not construct the Gin *gin.Engine in main: the framework creates it when the root command runs (consoleserver.NewCommandServer β†’ internal server startup).

To run middleware on all routes, register one or more func(*gin.Engine) hooks with consoleserver.WithEngineOptions when calling NewCommandServer. Each hook receives the engine after the stock middleware (recovery, OpenTelemetry, Prometheus, request logging, CORS, delay) and before services and API routes are wired.

var rootCmd = consoleserver.NewCommandServer(
"my-app",
"1.0.0",
"My Application",
consoleserver.WithEngineOptions(func(engine *gin.Engine) {
engine.Use(MyMiddleware())
}),
)

You can pass multiple callbacks; they run in order. For WithCommandOptions, custom top-level routes, and longer examples, see Command server options.

Custom middleware​

func RateLimiter() gin.HandlerFunc {
limiter := rate.NewLimiter(100, 200)

return func(ctx *gin.Context) {
if !limiter.Allow() {
util.RespondWithError(ctx, util.NewErrorMessage("E4291", "Rate limit exceeded"))
ctx.Abort()
return
}
ctx.Next()
}
}

router.Use(RateLimiter())

Middleware order (mental model)​

At a high level:

  1. Engine-level stock middleware (recovery, telemetry, logging, CORS, etc.) β€” see global middleware
  2. API group: default WithAuthentication (all routes need a valid session/token unless you use WithoutAuthentication)
  3. Per-route / per-group middleware such as RequirePermission
  4. Controller handler

Exact ordering is defined in the framework router setup; when in doubt, use the patterns above rather than re-stacking auth manually.

Best practices​

  • Prefer WithoutAuthentication for explicit public groups instead of bypassing security ad hoc
  • Use RequirePermission only where business rules need finer control than β€œlogged in”
  • Keep middleware small; call services from controllers
  • Always call ctx.Next() or ctx.Abort()
  • Return errors with util.RespondWithError consistently