I have recently released a new Haskell package called servant-rawm. It allows embedding a raw WAI Application inside a Servant API.

How does it work?

Below I will give a simple example of how servant-rawm can be used.

Imagine we have an API that looks like the following:

This represents a Servant API that can be accessed at http://localhost/serve-directory-example/ and does something. RawM represents any possible server that can be defined as a WAI Application. Without looking at the actual server definition, we are not sure what it does, so let's take a look at the server definition:

serverRoot is our main top-level Servant server. Since there is only one endpoint defined in Api, there is only one endpoint in the body of serverRoot: rawEndpoint.

rawEndpoint is a fileserver. It uses serveDirectoryWebApp to serve files from of a given directory. Note that the directory is obtained from the ReaderT monad.

serverRoot can be run with the following two functions:

app transforms our serverRoot Servant API into a WAI Application (which happens to also contain the Application from rawEndpoint). This uses the enter machinery provided by Servant.1

main uses Warp's run function to run app.

We can access this server with curl. Assuming the server has been run in the current directory, and the file ./some/path/foo.txt exists, it can be fetched like the following:

How is RawM different from Servant's Raw type?

RawM from servant-rawm is similar to, but more powerful than, Raw provided by Servant.

Here is what the code would look like if we were using Raw instead of RawM:

If you look at the body of the rawEndpoint' function, you can see that it is using the Tagged data constructor, which ends up with a type like the following type:

Notice that Tagged takes an Application and not a ReaderT Filepath IO Application. This means that the ReaderT cannot be used to create the Application.

Contrast the type of rawEndpoint from the previous section with the type of rawEndpoint':

You can see that RawM is strictly more powerful than Raw, because it allows the Application to be created monadically.

Client and Documentation

servant-rawm also provides two additional instances for RawM: HasClient (for use with servant-client) and HasDocs (for use with servant-docs).

More information can be found in the client example and the docs example in the source repository.


servant-rawm should be used whenever you need to use monadic effects to create an Application in a Servant API.


  1. Take a look at the Servant tutorial for an explanation of how enter works. This blog post is based on servant-0.11. Future versions of Servant will do away with enter. However, RawM will still work in a similar fashion. See this PR for more information.

tags: haskell