I use Persist and Servant. I have the following definitions in one module:
-- Type alias for convenience
type DbAction m a = ReaderT SqlBackend m a
-- Run database queries with a connection pool
runSql :: DbAction (NoLoggingT (ResourceT IO)) a -> ConnectionPool -> IO a
runSql = runSqlPersistMPool
Now, in my Servant API definition (different module), I have a bunch of these:
postUser :: UserCreate -> Handler Text
postUser user = liftIO $ flip runSql pool $ do
storeUser user
getUser :: PathUserId -> Handler (Maybe User)
getUser uid = liftIO $ flip runSql pool $ do
fetchUser $ extractUserId uid
I want to extract liftIO $ flip runSql pool $ ...
in a function so I don't repeat it all over the file.
For some reason I am stuck here and can't figure it out :(
EDIT
Something I tried:
runDbAction :: SqlPersistT IO a -> Handler a
runDbAction action = liftIO $ flip runSql pool $ action
however, SqlPersistT
exist only in Sqlite package, and I dont want to depend on it...
I ended up using
runDbAction :: SqlPersistM a -> Handler a
runDbAction action = liftIO $ runSql action pool
Something like this can be written without depending on particularly specialized libraries:
runDbAction :: MonadIO m => r -> ReaderT r IO a -> m a
runDbAction r = liftIO . flip runReaderT r
We have the following specialized definitions:
type SqlPersistT = ReaderT SqlBackend
newtype Handler a
instance MonadIO Handler
which means that runDbAction
can be used as if it had this type:
runDbAction :: SqlBackend -> SqlPersistT IO a -> Handler a
Of course, you can't bake pool
into the definition without depending on the package that gives you SqlBackend
.
(This answer assumes you are using persistent
to provide SqlPersistT
and servant-server
to provide Handler
.)