Open-source project
haskell-servant/servant avatar
haskell-servant/servant

servant: a Haskell DSL where one type gives you the server, the client and the docs

Servant is a Haskell DSL for describing, serving, querying, mocking, documenting web applications and more!

1,967 stars431 forksHaskellLicense varies

At a glance

What is it?
servant is a type-level web framework for Haskell in which a single API type is used to derive the server, the client, a mock implementation and the documentation. This looks at how the type-level machinery is organised across packages, and what it costs to adopt.
Who is it for?
servant fits a Haskell team that wants the server, the client and the API documentation generated from one description, and that wants a compile-time guarantee that the client and the server agree. It does not fit a team that wants to design the API as a schema first and generate code from it, since servant is code-first and the type is the source of truth, and it is a poor fit outside Haskell, because the whole design rests on type level programming.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Haskell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • haskell-servant/servant1,967 ★ · +0
Star history
Repository2026-10-162026-11-06
haskell-servant/servant2k2k

0 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The API type is the program

servant is described as a Haskell DSL for describing, serving, querying, mocking, documenting web applications and more, and the subtitle in the readme is shorter and more precise: a type-level web DSL.

The idea is that you describe your API once, as a Haskell type, and everything else is derived from it. The server is derived, the client is derived, the mock server for your tests is derived, and the documentation is derived. The end result is that a client and a server cannot disagree about the shape of a request or a response, because both are projections of the same type.

That guarantee is the reason to use the library, and it is a real one. In a stack where the server is written by one team and the client by another, the usual failure is a mismatch that shows up at run time, or a schema that drifts from the code. servant removes the category rather than the instance.

The documentation points at a tutorial that introduces the core features, and says that after it you should be able to write your first servant webservices, learning the rest from the haddocks' examples. The core documentation lives at docs.servant.dev, and blog posts, videos and slides are collected on the project website. Help is available on the `#haskell-servant` IRC channel on libera.chat and on the project mailing list.

The repository is under the `haskell-servant` organisation, its documentation site is docs.servant.dev, and the licence field is not stated in the repository metadata, while the `setup.py` in the root, which packages the documentation, declares BSD3.

The package split tells you what is a library and what is a choice

The repository is a directory per package, and the names describe the design rather than the implementation.

The core is `servant/`, and the server side is `servant-server/`, which contains the combinators that turn an API type into a running application. The client side is split into `servant-client/` and `servant-client-core/`, where the core holds the pieces that do not depend on a particular HTTP engine, and the client package adds the engine specific ones. `servant-client-ghcjs/` is the variant for compiling to JavaScript.

The alternative server stacks are separate packages, and that is the design decision worth understanding. `servant-conduit/`, `servant-pipes/`, `servant-http-streams/` and `servant-foreign/` are server backends built on conduit, pipes, http-streams and http-client respectively, and there are also the `servant-machines/` and `servant-foreign/` packages for the same reason. On the client side there is `servant-client-ghcjs/` for the browser, and `servant-auth/` for authentication.

The tooling packages are the interesting ones for an evaluation. `servant-swagger/` and `servant-openapi3/` generate documentation in those formats from the same type, `servant-quickcheck/` generates property based test data and mock servers, and `servant-docs/` holds the documentation site sources, with a `doc/` directory and a `changelog.d/` directory for pending changelog entries.

So the API type is a shared artefact, and the packages are the four consumers of it: serve it, call it, test it, and document it.

Nix shells, cabal, and stack all appear in the repository

The build tooling in the repository is plural, and that is a fact about the project rather than an accident.

There is a `flake.nix` and a `flake.lock` at the root, and the readme explains what the shells are for. The default shell provides the tools and dependencies needed to build the servant packages:

sh
$ nix develop

The tutorial and the cookbook have their own shell:

sh
$ nix develop '#tutorial'

and you can select a different GHC version with a third:

sh
$ nix develop '#ghc96'

Alongside that there is a `cabal.project`, a `cabal.ghcjs.project` for the JavaScript build, a `stack.yaml` with its lock file, and a `stack-ghcjs.yaml`. The `cabal.project` is also where the release process expects selective `allow-newer` entries, which is the mechanism a project uses to record that a specific dependency bound had to be relaxed.

There is also a `fourmolu.yaml` for the formatter and an `.hlint.yaml` for the linter, plus `changelog.d/` for pending changelog fragments, which is the convention of collecting changelog entries per change and assembling them at release time.

The release process is written down, tag by tag

The readme contains a release process outline attributed to phadej, and it is unusually concrete, which tells you the maintainers care about the release mechanics rather than leaving them to chance.

The first step is to update the changelog and bump versions in `master`, with a suggested starting point being `git log --oneline v0.12.. | grep 'Merge pull request'`, using the correct previous release tag.

Then a release branch is created, for example `release-0.13`, and the reason given is that the release branch is useful for backporting fixes from `master`.

The smoke test step uses a separate repository, servant-universe, with submodules, and the commands given are `git submodule foreach git checkout master` and `git submodule foreach git pull` to get the newest of everything, then `cabal new-build --enable-tests all` to verify that everything builds and `cabal new-test all` to run the tests. The documentation adds a good piece of advice: separate these steps, because tests often pass if they compile. It also gives the two options for a broken package, which are to fix it and open a pull request, testing against an older `servant` version as well, or to comment it out temporarily.

When the ripples are cleared, the release is tagged with `git tag -s`, the tags are pushed, and the package is uploaded with `cabal sdist` and `cabal upload`.

One thing to note: the repository has no GitHub releases, so the published versions live on the Haskell package index rather than as release artefacts on GitHub, and the version tags in the repository are the reference point for what exists.

The benchmark entry compares servant against warp and reitit

The maintainers develop and maintain the servant entry in the TechEmpower framework benchmarks, in the FrameworkBenchmarks repository, and the readme gives the exact commands.

To verify that the entry compiles and the tests pass:

sh
./tfb --mode verify --test servant servant-beam servant-psql-simple --type json plaintext db fortune

To compare against `warp`:

sh
./tfb --mode benchmark --test warp servant servant-beam servant-psql-simple --type json plaintext db fortune

And to compare with `reitit`, the Clojure framework:

sh
./tfb --mode benchmark --test reitit reitit-async reitit-jdbc servant servant-beam servant-psql-simple --type json plaintext db fortune

Three entries appear in those commands, and the difference between them is the server stack rather than the API: `servant`, `servant-beam` and `servant-psql-simple`. The test names, json and plaintext, db and fortune, are the standard TechEmpower scenarios.

The visualised results are on techempower.com, and the fact that the project maintains its own entry is the interesting part, because a benchmark entry that nobody updates goes stale, and a stale benchmark entry is worse than no entry. The repository also has a `streaming-benchmark.sh` script, so streaming is measured as well.

Where servant is the wrong tool

The costs of this approach are real, and they show up in three places.

The first is the learning curve, and it is a real one. Type-level programming in Haskell means writing a type that describes your endpoints, with the combinators encoding path, method, request body type, response body type and error type. A team comfortable with ordinary Haskell will still need the tutorial and the cookbook before they are productive, and the readme says as much. The compile times and the error messages are part of that cost, and the repository's own practice of using type-level Haskell for its internal API is a good argument for it, but it is an argument, not a proof that it is the right choice for you.

The second is the code-first versus schema-first question. servant derives the OpenAPI or Swagger document from your Haskell type. If your organisation's process is to design the API contract first, in a schema, and hand it to implementers, servant runs that process backwards, and there is a mismatch of method rather than of tooling. The same applies to a team where the client is written in another language, since the generated documentation is the only thing the other side gets, and a hand-written client cannot be checked against the Haskell type.

The third is the dependency footprint. Adopting servant means picking a server stack, and the readme's own benchmark commands show three of them side by side, so the choice of backing server stack is part of what you are adopting. Integrations are spread across the `servant-*` directories, which is a wide surface to keep current.

The final practical limit is versioning. With no GitHub releases and versions living on the Haskell package index, the upgrade path for a service is a cabal or stack dependency bump across a set of packages, and the changelog discipline in `changelog.d/` is what you will be reading to understand what changed.

Editorial conclusion

servant fits a Haskell team that wants the server, the client and the API documentation generated from one description, and that wants a compile-time guarantee that the client and the server agree. It does not fit a team that wants to design the API as a schema first and generate code from it, since servant is code-first and the type is the source of truth, and it is a poor fit outside Haskell, because the whole design rests on type level programming. Before you adopt it, read the tutorial and the cookbook, and pick a backend deliberately, since the concrete server choice is part of your dependency set rather than a configuration detail.

Frequently asked questions

What is servant in Haskell?

servant is a type-level web DSL for describing, serving, querying, mocking and documenting web applications. You write the API once as a Haskell type, and the server, the client, a mock server and the documentation are all derived from that type.

How do I set up a development environment for servant?

The repository ships a flake.nix, and the default shell provides the tools and dependencies for building the packages with `nix develop`. There is a separate shell for the tutorial and cookbook, `nix develop '#tutorial'`, and shells for other GHC versions such as `nix develop '#ghc96'`.

Which packages does servant publish?

The repository holds the core `servant` package, `servant-server`, `servant-client` and `servant-client-core`, alternative server backends such as `servant-conduit`, `servant-pipes` and `servant-http-streams`, `servant-auth`, documentation generators `servant-swagger` and `servant-openapi3`, and `servant-quickcheck` for mock servers and property based testing.

How are servant releases published?

The release outline says to bump versions and the changelog in master, create a release branch for backports, smoke test in the servant-universe repository with `cabal new-build --enable-tests all` and `cabal new-test all`, then tag with `git tag -s` and upload with `cabal sdist` and `cabal upload`. The repository itself publishes no GitHub releases.

How is servant benchmarked?

The project maintains its own TechEmpower framework benchmarks entry, with the verify command `./tfb --mode verify --test servant servant-beam servant-psql-simple --type json plaintext db fortune` and comparison modes against warp and reitit. The results are published on techempower.com.

Official sources

  1. haskell-servant/servant on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/haskell-servant-servant.svg)](https://hysenlabs.com/projects/haskell-servant-servant)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/haskell-servant-servant.svg)](https://hysenlabs.com/projects/haskell-servant-servant)

Was this useful?