I want to write my database access code for a Snap application in a way that makes the queries easy to test from the ghci
repl, while also working within the Snap context. My solution so far is to define a HasPostgres instance for IO and to write all my DB access functions following the pattern of allUsers
in the code snippet below. Am I doing this correctly?
{-# LANGUAGE OverloadedStrings #-}
module Database where
import Core
import Control.Applicative
import Database.PostgreSQL.Simple.FromRow (field, fromRow)
import Database.PostgreSQL.Simple.ToRow (toRow)
import Database.PostgreSQL.Simple.ToField (toField)
import Snap.Snaplet.PostgresqlSimple
import Data.Pool
import qualified Database.PostgreSQL.Simple as P
import Data.Text (Text)
import qualified Data.Text as T
import Control.Monad.CatchIO
instance FromRow User where
fromRow = User <$> field <*> field
instance ToRow User where
toRow (User a b) = [toField a, toField b]
allUsers :: (HasPostgres m) => m [User]
allUsers = query_ "SELECT user_id, email from users"
instance HasPostgres IO where
getPostgresState = do
let stripes = 1
let idle = 5
let resources = 20
let ci = P.ConnectInfo "localhost" 5432 "choi" "" "testDatabase"
pool <- createPool (P.connect ci) P.close stripes (realToFrac (idle :: Double)) resources
return $ Postgres pool
1 Answer 1
Yes, this looks good to me. I've done exactly the same thing to facilitate debugging in my projects. However, I would recommend that you put the IO instance in some special module where all your debugging code lives. You really do not want instances like this making their way into any other code because it is akin to making a global statement that IO always has a test database available at localhost on port 5432.