I have an API written using the Servant library which connects to a postgres db. The connection string for my db is stored in a configuration file. Everytime I make a request to any endpoint that interacts with the db I have to read the file to get the connection string, this is what im trying to avoid.
Step by step example of what im trying to achieve:
- Application starts up.
- Contents of the config file are read and bound to some type/object.
- I make a request to my endpoint to create an entry in the db.
- I read the connection string from the type/object that I bound it to and NOT the config file.
- Every subsequent request for the lifetime of the application does not have to read the config file everytime it wants to interact with the database.
Doing this in something like java/c# you would just bind the contents of a file to some POCO which would be added to your DI container as a singleton so it can be referenced anywhere in your application and persist between each api request. If I have 100 requests that ineract with the db, none of those 100 requests would need to read config file to get the connection string as it was already loaded into memory when the app started.
I have thought about using the cache package, but is there an easier way to do something like this without a third party package?
Let's begin with this trivial Servant server:
Suppose we don't want to hardcode that
1. We could turnfooServerinto a function likeThen, in
main, we could read thenfrom a file then callmakeFooServerto construct the server. Something similar could be done for your database connection.There's another approach that might be sometimes preferrable. Servant lets you define servers whose handlers live in a monad different from
Handler, and then transform them into regular servers (tutorial).We can write a server in which the handler monad is a
ReaderTholding the configuration:Where
ServerTis a more general form ofServerthat lets you specify the handler monad in an extra type argument.Then, we use the
hoistServerfunction to supply the initial environment and go back to a regular server:The
ServerT FooAPI (RHandler Int)approach has the advantage that you still have a server value that you can directly manipulate and pass around, instead of it being the result of a function.Also, for some advanced use cases, the environment might reflect information derived from the structure of each endpoint.