Skip to content

Middlewares

Middlewares are higher-order functions that enhance handlers. They accept a 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 ts = h (Request `With` ts) Response

type Middleware h tsOut tsIn = RequestHandler h tsIn -> RequestHandler h tsOut

Here is an example:

queryParam ::
  (Get h (RequiredQueryParam name val) Request, ArrowChoice h)
  => h (Request `With` ts, Either ParamNotFound ParamParseError) Response  -- ^ error handler
  -> Middleware h ts (RequiredQueryParam name val : ts)
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.

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 a lot more flexible than 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.