Using Selda with Servant

I came to Haskell late last year, and of course nowadays we’re mostly working with APIs and databases. In the Haskell ecosystem, I think the API part has already been figured out. We have Servant. The first time I worked with it I talked to myself: this is how you should be building API, request in, record out, nothing else. With Servant you will not have to worry about mundane tasks like adding correct header, method checking and content negotiation, .etc, just focus on writing the logic that really matter.

The SQL part though, is still messy. There’s Persistent, which is kind of like ActiveRecord, in the sense that you have little control over your queries, people use it with a separate query builder (Esqueleto) which is not ideal. There’s also postgresql-simple which seems loved by everyone, but it is pretty low level and certainly not type safe. I’m coming to Haskell for safety, and if it is not guaranteed I would just come back to Go. Until I met Selda, it feels just right. In my opinion it is at the right level of abstraction I want to have, not too low as postgresql-simple, and also not too high as persistent or groundhog. It has a unique representation of database records as “inductive tuple”, which can trace back to the theory of relational database design.

However, Selda is relatively new and I did not find any pointer to how to use it with Servant. I started with the Servant documentations about using Postgreql, which is not very helpful because it makes the database connection a parameter of the API. We’re using Haskell, we solve problems using monads, so I started to look at Servant’s custom monad tutorial.

Long story short, following is what I’ve come up with. Let say we’re building an API for a blog, here is the API definition in Servant:

Here’s my application monad:

To glue them together I defined an MonadSelda instance for my application monad (in this case I also used the RIO monad).

Then the API implementation:

You can see that getPostList and getPost are totally free of any details from the API, and you can compose them just like any other data fetch in other API handler. Suppose in a company we have different system with different database setup, we can just import the data fetch functions and compose them into an API with minimal effort.

In conclusion I really love the this combination. It really shows the power of Haskell type system and how language features like monad make integration seamless.