servant-generic alternatives and similar packages
Based on the "servant" category.
Alternatively, view servant-generic alternatives based on common mentions on social networks and blogs.
-
servant
Main repository for the servant libraries — DSL for describing, serving, querying, mocking, documenting web applications and more! -
servant-elm
Automatically derive Elm functions to query servant webservices -
servant-purescript
Translate servant API to purescript code, with the help of purescript-bridge. -
servant-swagger-ui
Provide embedded swagger UI for servant and swagger -
servant-response
Moved to http://github.com/haskell-servant -
servant-auth-cookie
Authentication via encrypted cookies -
servant-router
Servant router for non-server applications. -
servant-js
Automatically derive javascript functions to query servant webservices. -
servant-aeson-specs
Generically obtain tests for JSON serialization -
servant-github-webhook
Servant combinators for writing secure GitHub webhooks -
servant-cli
Generate a command line client from a servant API -
servant-pagination
Type-safe pagination for Servant APIs -
servant-matrix-param
Matrix parameter combinator for servant -
servant-pandoc
Render a servant API to Pandoc's native representation -
servant-auth-token-leveldb
Servant based API and server for token based authorisation -
servant-auth-token-acid
Servant based API and server for token based authorisation -
servant-mock
Derive a mock server for free from your servant API types -
servant-reason
Automatically derive bindings for Servant APIs in Reason -
servant-jsonrpc
Tools to build JSON-RPC clients and servers the Servant way -
servant-ruby
Create a Ruby client from a Servant API using Net::HTTP. -
servant-kotlin
Automatically derive Kotlin functions to query servant webservices -
servant-match
Standalone implementation of servant’s dispatching mechanism -
servant-options
Provide responses to OPTIONS requests for Servant applications. -
servant-http2-client
Generate http2-client from Servant APIs -
servant-haxl-client
automatical derivation of querying functions for servant webservices -
servant-proto-lens
Servant Content-Type for proto-lens protobuf modules. -
servant-generate
Generate default implementations for servers in a flexible way (a.k.a servant-mock on steroids) -
servant-multipart
multipart/form-data (e.g file upload) support for servant -
servant-jsonrpc-client
Generate JSON-RPC servant clients
Deliver Cleaner and Safer Code - Right in Your IDE of Choice!
Do you think we are missing an alternative of servant-generic or a related project?
README
This package has been merged into servant 0.14.1, please use that instead if available.
tl;dr
Specify Servant APIs with simple records instead of :<|>
trees.
data Site route = Site
{ about :: route :-
"about" :> Get '[PlainText] Text
, faq :: route :-
"faq" :> Get '[PlainText] Text
} deriving Generic
siteServer :: Site AsServer
siteServer = Site
{ about = return "about"
, faq = return "faq"
}
type Api = ToServant (Site AsApi)
main :: IO ()
main = run 31337 $ serve (Proxy :: Proxy Api) (toServant siteServer)
So long and thanks for all the :<|>
Servant is great for building typesafe APIs. However, its biggest weakness is that you need to specify APIs as a tree of :<|>
operators:
type SiteOld =
"about" :> Get '[PlainText] Text
:<|> "faq" :> Get '[PlainText] Text
:<|> "subsite" :> SubSiteOld
type SubSiteOld =
"echo" :> Capture "x" Text :> Get '[PlainText] Text
:<|> "timestwo" :> Capture "x" Int :> Get '[PlainText] Text
siteOldServer :: Server SiteOld
siteOldServer =
return "about"
:<|> subSiteOldServer
:<|> return "faq"
subSiteOldServer :: Server SubSiteOld
subSiteOldServer =
return
:<|> (\x -> return $ T.pack $ show (x * 2))
This is cumbersome because you need to remember the exact order of routes when you are implementing the API. Not only that, but if you get anything wrong, you are treated with an incomprehensible error message. Let's see what happens if we mix up the implementations of /faq
and /subsite
:
• Couldn't match type ‘(Text
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)
:<|> (Int
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)’
with ‘transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text’
Expected type: Server SiteOld
Actual type: transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text
:<|> (Server SubSiteOld
:<|> ((Text
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)
:<|> (Int
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)))
• In the expression:
return "about" :<|> subSiteOldServer :<|> return "faq"
In an equation for ‘siteOldServer’:
siteOldServer
= return "about" :<|> subSiteOldServer :<|> return "faq"
Can you figure out what the problem is from that?
What this package lets you do is to specify routes as regular Haskell records:
data Site route = Site
{ about :: route :-
"about" :> Get '[PlainText] Text
, faq :: route :-
"faq" :> Get '[PlainText] Text
, subSite :: route :-
"subsite" :> ToServant (SubSite AsApi) -- record APIs can be nested easily
} deriving Generic
data SubSite route = SubSite
{ echo :: route :-
"echo" :> Capture "x" Text :> Get '[PlainText] Text
, timesTwo :: route :-
"timestwo" :> Capture "x" Int :> Get '[PlainText] Text
} deriving Generic
type Api = ToServant (Site AsApi)
subSiteServer :: SubSite AsServer
subSiteServer = SubSite
{ echo = return
, timesTwo = \x -> return $ T.pack $ show (x * 2)
}
siteServer :: Site AsServer
siteServer = Site
{ about = return "about"
, faq = return "faq"
, subSite = toServant subSiteServer
}
Now everything is named so we don't need to remember which route is which. These records can be converted to Servant-compatible types like this:
ToServant (MyApiRecord AsApi)
and to Servant-compatible implementations like this:
toServant myApiRecordServer
These functions work with any library based on Servant, not just servant-server
.
What happens if we make the same mistake as above?
• Couldn't match type ‘(Text
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)
:<|> (Int
-> transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text)’
with ‘transformers-0.5.2.0:Control.Monad.Trans.Except.ExceptT
ServantErr IO Text’
Expected type: AsServer :- ("faq" :> Get '[PlainText] Text)
Actual type: ToServant (SubSite AsServer)
• In the ‘faq’ field of a record
In the expression:
Site
{about = return "about", faq = toServant subSiteServer,
subSite = return "faq",
home = return "So long and thanks for all the :<|>"}
In an equation for ‘siteServer’:
siteServer
= Site
{about = return "about", faq = toServant subSiteServer,
subSite = return "faq",
home = return "So long and thanks for all the :<|>"}
Now, GHC tells us that the problem is in the faq
field, and that we passed a SubSite
route rather than an "faq"
route.
This approach is based on my solga library, which also has these benefits and is a lot simpler than Servant.