serv-wai- Dependently typed API servers with Serv

Safe HaskellNone





The Server type is the core type generated by this module. It's essentially a StateT monad storing a Context accumulated over the routing process of the server.

newtype Server m Source

A server executing in a given monad. We construct these from Api descriptions and corresponding Impl descriptions for said Apis. Ultimately, a Server, or at least a 'Server IO', is destined to be transformed into a Wai Appliation, but Server tracks around more information useful for interpretation and route finding.



data ServerResult Source


RoutingError RoutingError

Routing errors arise when a routing attempt fails and, depending on the error, either we should recover and backtrack or resolve the entire response with that error.

WaiResponse Response

If the response is arising from the Server computation itself it will be transformed automatically into a Response value we can handle directly. These are opaque to routing, assumed successes.

Application (Context -> Application)

If the application demands an "upgrade" or ties into another server mechanism then routing at that location will return the (opaque) Application to continue handling.

Basic Servers

returnServer :: Monad m => m ServerResult -> Server m Source

Inject a monadic result directly into a Server

notFound :: Monad m => Server m Source

A Server which immediately fails with a NotFound error

badRequest :: Monad m => Maybe String -> Server m Source

A Server which immediately fails with a BadRequest error

methodNotAllowed :: Monad m => Set Verb -> Server m Source

A Server which immediately fails with a MethodNotAllowed error

orElse :: Monad m => Server m -> Server m -> Server m Source

Servers form a semigroup trying each Server in order and receiving the leftmost one which does not end in an ignorable error.

Or, with less technical jargon, m orElse n acts like m except in the case where m returns an ignorable Error in which case control flows on to n.

Transforming Servers

mapServer :: Monad m => (forall x. m x -> n x) -> Server m -> Server n Source

Lift an effect transformation on to a Server

Interpreting Servers

serverApplication :: Server IO -> Application Source

Converts a Server IO into a regular Wai Application value.

serverApplication' :: Server IO -> (Context -> Response -> Response) -> Application Source

Converts a Server IO into a regular Wai Application value; parameterized on a "response transformer" which allows a final modification of the Wai response using information gathered from the Context. Useful, e.g., for writing final headers.

serverApplication'' :: Server IO -> (Context -> ServerResult -> Response) -> Application Source

Converts a Server IO into a regular Wai Application value. The most general of the serverApplication* functions, parameterized on a function interpreting the Context and ServerResult as a Wai Response. As an invariant, the interpreter will never see an Application ServerResult---those are handled by this function.


defaultRoutingErrorResponse :: RoutingError -> Response Source

A straightforward way of transforming RoutingError values to Wai Responses. Used by default in serverApplication'.


As a Server runs it generates a Context descrbing the routing and decoding/encoding process so far. The Context provides valuable information aboue the Request and also about how the implemtation of the server has examined the Request so far.

data Context Source




ctxRequest :: Request

The original Request which this Context was initiated from. The requestBody here has been "frozen" so that even if it is accessed by the Server it can be accessed again later without impact.

ctxPathZipper :: ([Text], [Text])

The current location in the URI path. The Text segments in the first part of the tuple are those which have already been consumed/visited (in reverse order) and those in the second part are those which have yet to be visited.

ctxHeaders :: Map SomeHeaderName ByteString

An extraction of the headers in the Request

ctxHeaderAccess :: Map SomeHeaderName (Maybe Text)

A Map from headers which have been requested so far by the Server to, possibly, the values that these headers are expected to take.

ctxQuery :: Map Text (Maybe Text)

An extraction of the query parameters in the Request

ctxQueryAccess :: [Text]

A listing of query keys which have been accessed so far by the Server

ctxBody :: ByteString

The body of the Request, strictly read.

This is cached via strictRequestBody so that we don't have to deal with multiple request body pulls affecting one another; this defeats partial and lazy body loading, BUT the style of API description we're talking about here isn't really amenable to that sort of thing anyway.


makeContext :: Request -> IO Context Source

Construct a fresh context from a Request. Fully captures the (normally streamed) body so that repeated accesses in the server will all see the same body (e.g., allows for pure, strict access to the body later).

Contextual monads

Servers are just monads within a server-like Context---the Contextual class abstracts out several operations which we expect to occur in such a context.

class Contextual m where Source


fork :: m a -> m (a, Context) Source

Run a computation with the current state and return it without affecting ongoing state in this thread.

restore :: Context -> m () Source

Restore a Context.

getVerb :: m (Maybe Verb) Source

Return the HTTP verb of the current context

endOfPath :: m Bool Source

Return True if there are no further path segments

popSegment :: m (Maybe Text) Source

Pops a path segment if there are any remaining

popAllSegments :: m [Text] Source

Pops all remaining path segments

getHeader :: forall a n. HeaderDecode n a => Sing n -> m (Either String a) Source

Pulls the value of a header, attempting to parse it

expectHeader :: forall n. Sing n -> Text -> m Bool Source

Asserts that we expect a header to take a given value; returns the validity of that expectation.

getQuery :: QueryDecode s a => Sing s -> m (Either String a) Source

Pulls the value of a query parameter, attempting to parse it