servant-zeppelin-client alternatives and similar packages
Based on the "servant" category.
Alternatively, view servant-zeppelin 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-generic
Specify Servant routes with records -
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-js
Automatically derive javascript functions to query servant webservices. -
servant-router
Servant router for non-server applications. -
servant-aeson-specs
Generically obtain tests for JSON serialization -
servant-subscriber
When REST is not enough ... -
servant-exceptions
Extensible exceptions for servant -
servant-pandoc
Render a servant API to Pandoc's native representation -
servant-pagination
Type-safe pagination for Servant APIs -
servant-cassava
Servant CSV content-type for cassava -
servant-github-webhook
Servant combinators for writing secure GitHub webhooks -
servant-cli
Generate a command line client from a servant API -
servant-mock
Derive a mock server for free from your servant API types -
servant-matrix-param
Matrix parameter combinator for servant -
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-github
servant types to access the GitHub API v3 -
servant-py
Servant client generators for the Python language -
servant-reason
Automatically derive bindings for Servant APIs in Reason -
servant-jsonrpc
Tools to build JSON-RPC clients and servers the Servant way -
servant-xml
Servant support for the XML Content-Type. -
servant-match
Standalone implementation of servant’s dispatching mechanism -
servant-kotlin
Automatically derive Kotlin functions to query servant webservices -
servant-http2-client
Generate http2-client from Servant APIs -
servant-ruby
Create a Ruby client from a Servant API using Net::HTTP. -
servant-options
Provide responses to OPTIONS requests for Servant applications. -
servant-csharp
Generate servant client library for C# -
servant-generate
Generate default implementations for servers in a flexible way (a.k.a servant-mock on steroids) -
servant-iCalendar
servant support for iCalendar -
servant-haxl-client
automatical derivation of querying functions for servant webservices -
servant-wasm
Servant support for delivering WebAssembly -
servant-proto-lens
Servant Content-Type for proto-lens protobuf modules. -
servant-multipart
multipart/form-data (e.g file upload) support for servant -
servant-jsonrpc-client
Generate JSON-RPC servant clients
Clean code begins in your IDE with SonarLint
Do you think we are missing an alternative of servant-zeppelin-client or a related project?
README
servant-zeppelin 
Server Side Loading JSON
Overview
The point of servant-zeppelin is to enable server side loading of JSON data without having to write boilerplate.
Take the following Album
datatype for example, which is expanded in greater detail in the tests:
data Album =
Album { albumId :: AlbumId
, albumName :: String
, albumOwner :: PersonId
, albumPhotos :: [PhotoId]
}
It's often the case that we have such a datatype which carries foreign keys to other data, for example PersonId
and PhotoId
. The client application would probably not be able to do anything useful with the JSON response for Album
without making additional requests to the server to fetch more data about the album owner or photos. At the same time, it might not be worth the effort for the server to implement a new data type representing the join of Album
and Person
, or even worse all O(2n) possible combinations of dependencies. We introduce a new servant combinator to capture what we call Inflatable
data, meaning that there is a way to expand the data in some context. For example, if the above Album
is represented by a row in a postgres table, we probably already have functions laying around like
getPersonById :: PersonId -> PGMonad Person
getPersonById = ...
getPhotosByIds :: [PhotoId] -> PGMonad [Photo]
getPhotosByIds = ...
We can use these functions to implement our Inflatable
typeclass, e.g.
instance Inflatable PGMonad PersonId where
type Full PGMonad PersonId = Person
inflator = getPersonById
and similarly for [PhotoId]
. We can then indicate that Album
has dependencies on these datatypes like this:
instance HasDependencies Album '[PersonId, [PhotoId]] where
getDependencies album = albumOwner album &: albumPhotos album &: NilDeps
This gives us access to a new servant combinator SideLoad (deps :: [*])
which can be used at the end of a typed route in the following way:
...
:<|> Capture "album" AlbumId :> Get '[PlainText, JSON] Album :> SideLoad '[Person, [Photo]]
...
The semantics are similar to QueryFlag
-- the presence of the keyword sideload
in the query params, or the key value pair sideload=true
or sideload=1
, will trigger a response with the additional sideloaded data if the desired content type is application/json
. The absence of this flag returns the normal JSON
serialization. If the desired content type was PlainText
in this example, nothing out of the ordinary happens.
Here is an example of the different responses:
{
"albumId": 1,
"albumPhotos": [
1,
2
],
"albumName": "Vacations",
"albumOwner": 1
}
{
"data": {
"albumId": 1,
"albumPhotos": [
1,
2
],
"albumName": "Vacations",
"albumOwner": 1
},
"dependencies": {
"person": {
"personName": "Alice",
"personId": 1
},
"photos": [
{
"artistId": 1,
"photoCaption": "At the Beach.",
"photoId": 1
},
{
"artistId": 1,
"photoCaption": "At the Mountain.",
"photoId": 2
}
]
}
}
servant-zeppelin-server
Much of what was needed to understand the server component was explained above. In order to get the ToJSON
instances for your sideloaded data, it's sufficient to have ToJSON
instance for all the components and that the components of the dependencies are instances of a type family called NamedDependency
. More concretely, in the above example we would need
instance ToJSON Person
type instance NamedDependency = "person"
instance ToJSON [Photo]
type instance NamedDependency = "photos"
instance ToJSON Album
in order to derive the instance ToJSON (SideLoaded Album '[Person, [Photo]])
, which is sufficient to support the route.
The second component which was needed is a way to transfer the context of the inflation to servant's Handler
monad. Concretely, if our PGMonad
above was newtyped around something like ReaderT Connection (ExceptT QueryError IO)
, we need to provide a natural transformation of type PGMonad :~> Handler
to the Context
when we define our application. In principle it might happen that you use different contexts for different datatypes, for example if you were maintaining two seprate databases. This is ok as long as you provide both transformations to the Context
. You can see the tests for more details.
servant-zeppelin-swagger
In order to have the swagger docs generate for an endpoing using the ... a :> SideLoad deps
combinator, we need to have a ToSchema
instance for SideLoaded a deps
. The will be automatically derived for you with sensible choices proveded that you have a ToSchema
instance for both a
and every d
in deps
. With swagger, a picture is worth more than code, but you can see the tests for how to generate this:
servant-zeppelin-client
We also provide a HasClient
instance for the SideLoad deps
combinator, though it behaves a little bit differenty than most HasClient
instances you might have encountered so far. The problem is that a real HasClient
instance is dependently typed-- if you give me sideload=true
param, I exepect the sideloaded data of type SideLoaded a deps
, and if it's not there I expect something of type a
. In order to get around the type systems limitations, the HasClient
instance provides us with a dependently typed function which takes a singleton boolean to get the desired type.
Also, the client has a bias for using requesting JSON-- this seems fair to me because there is yet no reason to use a SideLoad
combinator on a route without JSON as a valid mime type. The necessary FromJSON
instances are supplied in the client lib as well, and as usual they can be automatically derived so long as the components all have instances.
The client library also exposes a typeclass ProjectDependency
implementing a single method projectDependency
. This has the same semantics as servant's HasContext
typeclass, and can be useful to get data out of a side loaded response.
Again, see the tests for examples.