Skip to content

Middlewares

Middlewares are higher-order functions that enhance handlers. They accept another handler as a function argument and produce a handler as the result.

Request Middlewares

A middleware can enhance a handler by probing for traits in the request.

type RequestHandler h req = h (Linked req Request) Response

type Middleware h reqOut reqIn = RequestHandler h reqIn -> RequestHandler h reqOut

Here is an example:

queryParam ::
  (Get h (RequiredQueryParam name val) Request, ArrowChoice h)
  => h (Linked req Request, Either ParamNotFound ParamParseError) Response  -- ^ error handler
  -> Middleware h req (RequiredQueryParam name val : req)
queryParamHandler errorHandler nextHandler = proc request -> do
  result <- probe QueryParam -< request
  case result of
    Left err -> errorHandler -< (request, err)
    Right val -> nextHandler -< val

This middleware chooses between nextHandler and errorHandler based on the result of probing the QueryParam trait. In this case, nextHandler has an additional trait RequiredQueryParam name val in its request input.

Response Middlewares

Middlewares can also enhance responses. Here is an example middleware that sets a response body:

setBody ::
  Set h (Body body) Response
  => HTTP.MediaType
  -> h a (Linked ts Response)
  -> h (body, a) (Linked (Body body : ts) Response)
setBody mediaType prevHandler = proc (body, a) -> do
  r <- prevHandler -< a
  plant (Body (Just mediaType)) -< (r, body)

The middleware first invokes prevHandler and then sets the body on the response produced by it.

More Use Cases

Middlewares are useful because we can chain a sequence of middlewares to produce a handler as shown in this example:

jwtAuth jwk
  $ queryParam @"limit" @Int errorHandler
  $ queryParam @"offset" @Int errorHandler
  $ myHandler

Keep in mind that middlewares are not limited to the patterns shown above; there are many other types of transformations possible. For example, you can have a middleware that captures a correlation ID, or generate a new one and inject it as a request trait. When the underlying handler returns a response, this middleware can add a response header with the correlation ID.