# Structuring APIs In this recipe, we will see a few simple ways to structure your APIs by splitting them up into smaller "sub-APIs" or by sharing common structure between different parts. Let's start with the usual throat clearing. ``` haskell {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE KindSignatures #-} import Data.Aeson import GHC.Generics import GHC.TypeLits import Network.Wai.Handler.Warp import Servant ``` Our application will consist of three different "sub-APIs", with a few endpoints in each of them. Our global API is defined as follows. ``` haskell type API = FactoringAPI :<|> SimpleAPI "users" User UserId :<|> SimpleAPI "products" Product ProductId ``` We simply join the three different parts with `:<|>`, as if each sub-API was just a simple endpoint. The first part, `FactoringAPI`, shows how we can "factor out" combinators that are common to several endpoints, just like we turn `a * b + a * c` into `a * (b + c)` in algebra. ``` haskell -- Two endpoints: -- - GET /x/[?y=] -- - POST /x/ type FactoringAPI = "x" :> Capture "x" Int :> ( QueryParam "y" Int :> Get '[JSON] Int :<|> Post '[JSON] Int ) {- this is equivalent to: type FactoringAPI' = "x" :> Capture "x" Int :> QueryParam "y" Int :> Get '[JSON] Int :<|> "x" :> Capture "x" Int :> Post '[JSON] Int -} ``` You can see that both endpoints start with a static path fragment, `/"x"`, then capture some arbitrary `Int` until they finally differ. Now, this also has an effect on the server for such an API, and its type in particular. While the server for `FactoringAPI'` would be made of a function of type `Int -> Maybe Int -> Handler Int` and a function of type `Int -> Handler Int` glued with `:<|>`, a server for `FactoringAPI` (without the `'`) reflects the "factorisation" and therefore, `Server FactoringAPI` is `Int -> (Maybe Int -> Handler Int :<|> Handler Int)`. That is, the server must be a function that takes an `Int` (the `Capture`) and returns two values glued with `:<|>`, one of type `Maybe Int -> Handler Int` and the other of type `Handler Int`. Let's provide such a server implementation, with those "nested types". **Tip**: you can load this module in ghci and ask for the concrete type that `Server FactoringAPI` "resolves to" by typing `:kind! Server FactoringAPI`. ``` haskell factoringServer :: Server FactoringAPI factoringServer x = getXY :<|> postX where getXY Nothing = return x getXY (Just y) = return (x + y) postX = return (x - 1) ``` If you want to avoid the "nested types" and the need to manually dispatch the arguments (like `x` above) to the different request handlers, and would just like to be able to declare the API type as above but pretending that the `Capture` is not factored out, that every combinator is "distributed" (i.e that all endpoints are specified like `FactoringAPI'` above), then you should look at `flatten` from the [servant-flatten](https://hackage.haskell.org/package/servant-flatten) package. Next come the two sub-APIs defined in terms of this `SimpleAPI` type, but with different parameters. That type is just a good old Haskell type synonym that abstracts away a pretty common structure in web services, where you have: - one endpoint for listing a bunch of entities of some type - one endpoint for accessing the entity with a given identifier - one endpoint for creating a new entity There are many variants on this theme (endpoints for deleting, paginated listings, etc). The simple definition below reproduces such a structure, but instead of picking concrete types for the entities and their identifiers, we simply let the user of the type decide, by making those types parameters of `SimpleAPI`. While we're at it, we'll put all our endpoints under a common prefix that we also take as a parameter. ``` haskell -- Three endpoints: -- - GET / -- - GET // -- - POST / type SimpleAPI (name :: Symbol) a i = name :> ( Get '[JSON] [a] :<|> Capture "id" i :> Get '[JSON] a :<|> ReqBody '[JSON] a :> Post '[JSON] NoContent ) ``` `Symbol` is the [kind](https://wiki.haskell.org/Kind) of type-level strings, which is what servant uses for representing static path fragments. We can even provide a little helper function for creating a server for that API given one handler for each endpoint as arguments. ``` haskell simpleServer :: Handler [a] -> (i -> Handler a) -> (a -> Handler NoContent) -> Server (SimpleAPI name a i) simpleServer listAs getA postA = listAs :<|> getA :<|> postA {- you could alternatively provide such a definition but with the handlers running in another monad, or even an arbitrary one! simpleAPIServer :: m [a] -> (i -> m a) -> (a -> m NoContent) -> ServerT (SimpleAPI name a i) m simpleAPIServer listAs getA postA = listAs :<|> getA :<|> postA and use 'hoistServer' on the result of `simpleAPIServer` applied to your handlers right before you call `serve`. -} ``` We can use this to define servers for the user and product related sections of the API. ``` haskell userServer :: Server (SimpleAPI "users" User UserId) userServer = simpleServer (return []) (\userid -> return $ if userid == 0 then User "john" 64 else User "everybody else" 10 ) (\_user -> return NoContent) productServer :: Server (SimpleAPI "products" Product ProductId) productServer = simpleServer (return []) (\_productid -> return $ Product "Great stuff") (\_product -> return NoContent) ``` Finally, some dummy types and the serving part. ``` haskell type UserId = Int data User = User { username :: String, age :: Int } deriving Generic instance FromJSON User instance ToJSON User type ProductId = Int data Product = Product { productname :: String } deriving Generic instance FromJSON Product instance ToJSON Product api :: Proxy API api = Proxy main :: IO () main = run 8080 . serve api $ factoringServer :<|> userServer :<|> productServer ``` This program is available as a cabal project [here](https://github.com/haskell-servant/servant/tree/master/doc/cookbook/structuring-apis).