I've been trying to get a web server running with scotty that can communicate with my db, using selda. I thought using a monad transformer stack would be the way to accomplish something like that. I've been trying to work it out, but I've run into a few dead-ends where the types just don't seem workable.
{-# LANGUAGE DeriveGeneric, OverloadedStrings, OverloadedLabels #-}
module Server where
import Web.Scotty
import Data.Monoid (mconcat)
import Data.Aeson (ToJSON)
import GHC.Generics
import Web.Scotty
import Database.Selda
import Database.Selda.SQLite
import Control.Monad.Trans.Class
import Models
type App = SeldaT SQLite ScottyM
-- withPersist:: (MonadIO m, MonadMask m) => SeldaT SQLite m a -> m a
server = scotty 4200 (withPersist router)
router :: App ()
router = do
lift $ get "/book/:id" searchBook
searchBook:: ActionM ()
searchBook = do
books <- query selectBookQuery
json books
where
selectBookQuery = do
book <- select goodreadsBooks
restrict (book ! #goodreadsId .== "20")
return book
I tried to base it loosely off the answer here, but I wanted to wrap the router instead of individual routes. I wouldn't want the # of connections I have open to be proportional to the # of routes I have, and I don't want each route to have to have a withPersist
call, if I can avoid it.
So I've got an App
which is of type SeldaT Sqlite ScottyM
, and using withPersist
(which is == withSQLite "mydb.db"
), I'd turn that SeldaT Sqlite ScottyM
into a ScottyM
. There are quite a few issues though, here's my understanding of them:
SeldaT m a
is constrained by(MonadIO m, MonadMask m)
, and ScottyM has no instance ofMonadIO
Scotty.get
returns aScottyM ()
, I feel like this is where I'd uselift
to make that into aSeldaT Sqlite ScottyM
, but I get an error about it, maybe related toScottyM
not being an instance ofMonadIO
from above.- Since
searchBook
is still anActionM
, I can't run queries inside it. Unsure how to getget
to accept my transformer stack instead of anActionM
Here are the errors:
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:19:23: error:
• No instance for (MonadIO ScottyM)
arising from a use of ‘withPersist’
• In the second argument of ‘scotty’, namely ‘(withPersist router)’
In the expression: scotty 4200 (withPersist router)
In an equation for ‘server’:
server = scotty 4200 (withPersist router)
|
19 | server = scotty 4200 (withPersist router)
| ^^^^^^^^^^^^^^^^^^
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:23:3: error:
• No instance for (MonadTrans (SeldaT SQLite))
arising from a use of ‘lift’
• In a stmt of a 'do' block: lift $ get "/book/:id" searchBook
In the expression: do lift $ get "/book/:id" searchBook
In an equation for ‘router’:
router = do lift $ get "/book/:id" searchBook
|
23 | lift $ get "/book/:id" searchBook
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:27:12: error:
• No instance for (MonadSelda
(Web.Scotty.Internal.Types.ActionT
Data.Text.Internal.Lazy.Text IO))
arising from a use of ‘query’
• In a stmt of a 'do' block: books <- query selectBookQuery
In the expression:
do books <- query selectBookQuery
json books
In an equation for ‘searchBook’:
searchBook
= do books <- query selectBookQuery
json books
where
selectBookQuery
= do book <- select goodreadsBooks
....
|
27 | books <- query selectBookQuery
| ^^^^^^^^^^^^^^^^^^^^^
UPDATE: I probably need to use ScottyT, after looking at some more similar questions. Not sure how to nest SeldaT in the ScottyT transformer though.