In zio-http
, one has the option to write middleware that provide some sort of context around a request; as a concrete example, we might think of an auth middleware that looks up some package of session information before processing a request:
case class UserSession(sessionId: UUID, username: String)
val authMiddleware: HandlerAspect[Any, UserSession] = HandlerAspect.customAuthProvidingZIO { request =>
ZIO.succeed(request.headers.get("X-Session-ID").flatMap {
case None => ZIO.succeed(None)
case Some(sessionId) => fetchSession(sessionId).catchAll(_ => ZIO.succeed(None))
}
}
We can attach this middleware to a Routes
to ensure that any request coming in with no X-Session-ID
header, or a session ID that our session cache doesn't know about, gets rejected. My question is, if the middleware succeeds in producing a UserSession
, how can I get that value and use it in the request handler, something like
// this doesn't work
Routes(
Method.GET / "foo" -> handler { (userSession: UserSession, request: Request) =>
fooDatabase.getAll(userSession.username)
}
) @@ authMiddleware
One reason this doesn't work, of course, is that the middleware isn't even attached to the Routes
until after it's created, so there's something of a chicken-and-egg problem; but that seems to be kinda the point of middleware, and this GitHub issue and this PR have titles at least that suggest that they're trying to achieve something similar to this, though I don't see anything exactly like what I'm looking for in the PR code.
Instead of attaching the middleware/
HandlerAspect
to the "outside" of theRoutes
as in the snippet in the question, it turns out that it's possible to reference the middleware in the definition of eachRoute
itself:(Kudos to user
zzyzzyxx
on the ZIO Discord for this find.)