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.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.