liveBook · Manning

Middleware

As explained in Learn Haskell by Example

Middleware is a function or component that wraps a web application to inspect or transform requests and responses before delegating to the wrapped application.

Middleware is a function that wraps a WAI Application, allowing a web server to inspect a request, produce a response, or delegate to the wrapped application. Since an Application receives a Request and response callback, Middleware has the shape Application -> Application. section 17.2.2 section 17.4

type Application = Request -> (Response -> IO ResponseReceived) ->
 IO ResponseReceived

Read the Application type as a request-handling function: it receives a request and a callback used to deliver the response. section 17.2.2

type Middleware = Application -> Application

The Middleware type makes wrapping explicit: provide an Application and return another Application. section 17.2.2

Adding an HTML root to a Servant API

A todo-list server can keep its typed Servant API unchanged while Middleware handles GET requests at /. The middleware checks requestMethod and pathInfo. For the root request, it loads and sorts todo entries, renders HTML as UTF-8, and calls responseLBS with a successful response and text/html content type. For every other request, it runs app req responseAct, preserving the API server's behavior. The server composes both layers with run port (mkAppRoot dbConn $ mkApiServer dbConn). section 17.2.2 section 17.2.3

Listing 17.10 A Middleware to render the todo list as HTML on the application’s root
mkAppRoot :: Connection -> Middleware
mkAppRoot dbConn app req responseAct
  | isRootRequest = do
      html <- rootHandler dbConn              #1
      responseAct $ responseLBS ok200 headers
 (H.renderHtml html)                            #2
  | otherwise = app req responseAct     #3
  where
    headers = [(hContentType,
 "text/html; charset=utf-8")]        #4
    isRootRequest =
      requestMethod req == methodGet   #5
        && null (pathInfo req)         #5

Follow the complete middleware shape: handle the matching root request, construct the response, and delegate all other requests. section 17.2.3

When adding middleware, keep these practical points in view:

  • Match narrowly: the example handles only GET requests whose path is the application root. section 17.2.2
  • Delegate unmatched requests to the wrapped application so existing API endpoints continue to work. section 17.2.3
  • Recognize the trade-off: modifying an Application this way loses the static guarantee that another GET / endpoint is not being intercepted. section 17.2.3

How Learn Haskell by Example covers it

How deep it goes
Defines the Application -> Application shape and works through request matching, response construction, delegation, and composition.
Examples
Uses a todo-list root page that sorts database entries, renders HTML, and leaves other requests to the API server.
What it assumes
Assumes familiarity with Servant APIs, WAI Applications, HTTP requests, callbacks, and basic Haskell functions.
What you can do after
Build it from scratch by wrapping an Application, matching requests, creating responses, and delegating unmatched traffic.
How it connects
Connects WAI Applications with Servant APIs, HTTP requests, response callbacks, HTML rendering, and database-backed handlers.

Also explained in

Compare how 10 books explain middleware →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.