Skip to content

Middleware Overview

In Apion, middleware is just a Handler — there’s no separate middleware type. A middleware handler typically returns Continue(modifiedRequest) to pass control to the next handler, but can also return Complete, Fail, or Skip.

Applies to all requests:

server
.use(LoggingMiddleware())
.use(CorsMiddleware())

Applies only to routes matching the prefix:

server.use("/api", authMiddleware)

Applies only within a router:

val adminRouter = Router()
.use(requireAdmin)
.get("/dashboard", dashboardHandler)
server.use("/admin", adminRouter)

Chain handlers for a specific route:

server.get("/protected", authMiddleware, handler)

Add data to the context for downstream handlers:

val withUser: Handler = request => {
val user = lookupUser(request.params("id"))
Future.successful(Continue(
request.copy(context = request.context + ("user" -> user))
))
}

Return a response immediately, stopping the chain:

val requireAuth: Handler = request =>
request.header("authorization") match {
case Some(_) => Future.successful(Continue(request))
case None => "Unauthorized".asText(401)
}

Use finalizers to modify the response after a handler completes:

val addHeader: Handler = request => {
val finalizer: Finalizer = (_, response) =>
Future.successful(response.copy(
headers = response.headers.add("X-Custom", "value")
))
Future.successful(Continue(request.addFinalizer(finalizer)))
}
  1. Global middleware runs in registration order
  2. Path-scoped middleware runs if the path matches
  3. Route handlers run if the method and path match
  4. Finalizers run in LIFO order on the response
MiddlewarePurpose
AuthMiddlewareJWT authentication with RBAC
CorsMiddlewareCross-origin resource sharing
SecurityMiddlewareSecurity headers
LoggingMiddlewareRequest/response logging
CompressionMiddlewareResponse compression
StaticMiddlewareStatic file serving
CookieMiddlewareCookie management
RateLimiterMiddlewareRequest throttling
FileUploadMiddlewareMultipart file uploads
BodyLimitMiddlewareRequest body size limits