Search code examples
haskellservant

Haskell servant combined api implementation


I am going through the servant tutorial found here. There is a partial implementation that is left for the user to figure out what it should be.

type APIFor a i =
       Get '[JSON] [a]
  :<|> ReqBody '[JSON] a :> PostNoContent
  :<|> Capture "id" i :>
         ( Get '[JSON] a
      :<|> ReqBody '[JSON] a :> PutNoContent
      :<|> DeleteNoContent
         )

serverFor :: Handler [a]
          -> (a -> Handler NoContent)
          -> (i -> Handler a)
          -> (i -> a -> Handler NoContent)
          -> (i -> Handler NoContent)
          -> Server (APIFor a i)
serverFor = error "..." -- What should be here?

Following from the previous examples I came up with the implementation:

serverFor = listing :<|> ops

    where
        listing :: Handler [a]
        listing = error ""

        ops a i =
            creating a
            :<|> getById i
            :<|> updating i a
            :<|> deleting i

            where
                creating :: a -> Handler NoContent
                creating a = error ""

                getById :: i -> Handler a
                getById id = error ""

                updating :: i -> a -> Handler NoContent
                updating i a = error ""

                deleting :: i -> Handler NoContent
                deleting i = error ""

But am getting the error:

• Couldn't match expected type ‘Handler [a]
                                -> (a -> Handler NoContent)
                                -> (i -> Handler a)
                                -> (i -> a -> Handler NoContent)
                                -> (i -> Handler NoContent)
                                -> Server (APIFor a i)’
              with actual type ‘Handler [a0]
                                :<|> (a1
                                      -> i0
                                      -> Handler NoContent
                                         :<|> (Handler a2
                                               :<|> (Handler NoContent :<|> Handler NoContent)))’
• Possible cause: ‘(:<|>)’ is applied to too many arguments
  In the expression: listing :<|> ops

I understand what the error is saying but do not know how to implement it correctly. Is anyone able to help with what the correct implementation should be?


Solution

  • The signature of the serverFor function says it takes handler functions and builds the Server.

    serverFor list create get update delete = 
      -- combine handlers according to API type
      list :<|> create :<|> (\i -> get i :<|> update i :<|> delete i)
    

    you can then call serverFor for your specific types.

    data User = User {id :: Int}
    
    server :: Server (APIFor User Int)
    server = serverFor listing creating getById updating deleting
      where
        listing :: Handler [User]
        listing = error ""
        creating :: User -> Handler NoContent
        creating a = error ""
        getById :: Int -> Handler User
        getById id = error ""
        updating :: Int -> User -> Handler NoContent
        updating i a = error ""
        deleting :: Int -> Handler NoContent
        deleting i = error ""