Swap the DB based on a config file

Swappable DB backend

So today, someone in #haskell-beginners asked a question about switching the database for an application based on a config file. Let's see a fairly simple solution for this problem. This is meant to be an intermediate tutorial. You should be comfortable with newtypes, typeclasses, monads and perhaps a little bit of json handling but not much more.

Step 1: define an interface for DB operations

First, let's define a typeclass for all database operations. The application will only use these operations and this typeclass. We don't want to put anything specific to a database there since it should be swappable.

Only one method there to keep things simple, but you can add many more.

Step 2: Create a simple application using this interface

Let's create a very simple application using that.

Step 3: Run the application with a postgresql backend

The basic application will be using the readerT pattern.

A simple wrapper to get access to a persistent connection to the database. This could also be a connection pool, or whatever is needed.

Now, we need to declare an instance of MonadDB for this application.

And now, let's run all of that:

Step 4: Get a config file in place

So far, everything was hardcoded. Let's introduce a simple config file in json format.

The two extensions DeriveAnyClass and DeriveGeneric allow us to get a free FromJSON instance. Here's an example of a simple json file:

Let's write some simple code to make sure everything is working as expected:

LambdaCase is my favorite extension, it allows to do a case ... of without having to conjure a name just for the sake of matching on it. ScopedTypeVariables is required there to let GHC know what is the type of the thing we're trying to decode.

Now, let's plug the configuration in the application:

Step 5: Extend the configuration

Now, to connect to another DB based on the configuration, we need to extend the config a little bit:

The config file now must have a field type to specify the database to use. The other fields are the one required for the specific DB.

Step 6: choose the implementation based on the config

Conclusion

This is a fairly straightforward way to have multiple swappable DB for an haskell application. It's also pretty easy to test as a result, just use an in-memory DB.