How do I pass the database connection to my HTTP handlers?
Now I have to pass the database connection to each route handler so they can
). With more than 10 routes, this will be very annoying. I know that I could define route handlers inside the main method and the connection handle
would be in scope, but that doesn't scale either.
I would like to be able to define top-level functions so that I can put them in different files. How can I clear this code?
Part of my brain knows monads can do this, but how? Scotty uses a monad
and RethinkDB has a monad too, but I'm not sure how to combine them.
LANGUAGE OverloadedStrings #-} LANGUAGE ScopedTypeVariables #-} import Web.Scotty import Courses.Course import qualified Database.RethinkDB as R import Control.Monad.IO.Class (liftIO) import Courses.Connection main :: IO () main = do h <- connectDb scotty 3000 $ do get "/" info get "/courses" (coursesAll h) info :: ActionM () info = text "Courses v1" coursesAll :: R.RethinkDBHandle -> ActionM () coursesAll h = do courses <- liftIO $ R.run h coursesTable json $ (courses :: [Course]) connectDb :: IO (R.RethinkDBHandle) connectDb = do connection <- R.connect "localhost" 28015 Nothing let connectionDb = R.use connection (R.db "courses") return connectionDb
UPDATE: RethinkDB turned out not to be appropriate. Actually, I want to know how to pass global config to my routes. For example:
LANGUAGE OverloadedStrings #-} import qualified Web.Scotty import Web.Scotty.Trans import Data.Text.Lazy import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Reader import Control.Monad.Trans data Config = Config Text main :: IO () main = do let config = Config "Hello World" how to I make this line work? scottyT 3000 id id routes routes :: ScottyT Text (ReaderT Config IO) () routes = do get "/" info info :: ActionT Text (ReaderT Config IO) () info = do this part seems like it works! Config message <- lift ask text $ "Info: " `append` message
source to share
You are almost gone with an updated question, the only thing you need is to provide two runners functionality
. So let's take a look at the signature
scottyT :: (Monad m, MonadIO n) => Port (forall a. m a -> n a) (m Response -> IO Response) ScottyT e m () n ()
is the monad you want to push onto the stack
is the output you want at startup
In your case,
ReaderT Config IO
(forall a. m a -> n a)
is a function that converts any computation
ReaderT Config IO a
, which we can easily do with
, so let's define
let readerToIO ma = runReaderT ma config
Next, we need a function to turn
, but since this is the
, we can simply reuse the above function
main = do let config = Config "Hello World" readerToIO ma = runReaderT ma config scottyT 3000 readerToIO readerToIO routes
source to share