diff --git a/snap-app.cabal b/snap-app.cabal
--- a/snap-app.cabal
+++ b/snap-app.cabal
@@ -1,5 +1,5 @@
 name:                snap-app
-version:             0.2.2
+version:             0.3.0
 synopsis:            Simple modules for writing apps with Snap, abstracted from hpaste.
 homepage:            Simple modules for writing apps with Snap, abstracted from hpaste.
 license:             BSD3
@@ -11,6 +11,7 @@
 cabal-version:       >=1.8
 
 library
+  ghc-options:       -O2
   hs-source-dirs:    src
   exposed-modules:   Snap.App, Snap.App.Types, Snap.App.Controller, Snap.App.Model, Snap.App.Migrate,
                      Data.Pagination, Text.Blaze.Extra, Text.Blaze.Pagination,
@@ -20,7 +21,7 @@
   build-depends:     base >= 4 && <5,
                      snap-core,
                      network,
-                     pgsql-simple >= 0.1.2,
+                     postgresql-simple,
                      mtl,
                      blaze-html == 0.4.3.4,
                      safe,
diff --git a/src/Snap/App/Controller.hs b/src/Snap/App/Controller.hs
--- a/src/Snap/App/Controller.hs
+++ b/src/Snap/App/Controller.hs
@@ -18,6 +18,7 @@
   where
 
 import Snap.Core
+import Snap.App.Model (Pool,withPoolConnection)
 import Snap.App.Types
 
 import Control.Applicative
@@ -29,8 +30,7 @@
 import Data.Pagination
 import Network.URI
 import Data.Text.Lazy             (Text,toStrict)
-import Database.PostgreSQL.Base   (withPoolConnection,withTransaction)
-import Database.PostgreSQL.Simple (Pool)
+import Database.PostgreSQL.Simple
 import Safe                       (readMay)
 import Text.Blaze                 (Html)
 import Text.Blaze.Renderer.Text   (renderHtml)
@@ -40,7 +40,6 @@
 runHandler :: s -> c -> Pool -> Controller c s () -> Snap ()
 runHandler st conf pool ctrl = do
   withPoolConnection pool $ \conn -> do
-    withTransaction conn $ do
       let state = ControllerState conf conn st
       -- Default to HTML, can be overridden.
       modifyResponse $ setContentType "text/html"
diff --git a/src/Snap/App/Migrate.hs b/src/Snap/App/Migrate.hs
--- a/src/Snap/App/Migrate.hs
+++ b/src/Snap/App/Migrate.hs
@@ -10,7 +10,7 @@
 import GHC.Int
 
 -- | Migrate the DB to the latest version.
-migrate :: Bool -> [(Int,Model c s Integer)] -> Model c s ()
+migrate :: Bool -> [(Int,Model c s Int64)] -> Model c s ()
 migrate create versions = go where
   go = do
     when create $ void $ ensureExists
diff --git a/src/Snap/App/Model.hs b/src/Snap/App/Model.hs
--- a/src/Snap/App/Model.hs
+++ b/src/Snap/App/Model.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE RecordWildCards #-}
 {-# OPTIONS -Wall #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE FlexibleContexts #-}
@@ -11,28 +12,27 @@
   ,single
   ,singleNoParams
   ,queryNoParams
-  ,processQuery
-  ,queryProcessed
+  ,withPoolConnection
   ,exec
-  ,DB.Only(..))
+  ,DB.Only(..)
+  ,newPool
+  ,Pool)
   where
 
+import           Control.Concurrent
+import           Control.Monad.CatchIO as E
 import           Control.Monad.Env                       (env)
 import           Control.Monad.Reader
 import           Data.String
-import           Database.PostgreSQL.Base   (withPoolConnection,withTransaction)
-import           Database.PostgreSQL.Simple              (Only(..),ProcessedQuery,Query)
-import qualified Database.PostgreSQL.Simple              as DB
-import           Database.PostgreSQL.Simple (Pool)
-import           Database.PostgreSQL.Simple.QueryParams
-import           Database.PostgreSQL.Simple.QueryResults
+import qualified Database.PostgreSQL.Simple as DB
+import           Database.PostgreSQL.Simple hiding (query)
+import           GHC.Int
 import           Snap.App.Types
 
 -- | Run a model action at the top-level.
 runDB :: s -> c -> Pool -> Model c s () -> IO ()
 runDB st conf pool mdl = do
   withPoolConnection pool $ \conn -> do
-    withTransaction conn $ do
       let state = ModelState conn st conf
       -- Default to HTML, can be overridden.
       runReaderT (runModel mdl) state
@@ -41,25 +41,14 @@
 model :: AppLiftModel c s => Model c s a -> Controller c s a
 model = liftModel
 
--- | A version of 'query' that does not perform query substitution.
-queryProcessed :: (QueryResults r) => ProcessedQuery r -> Model c s [r]
-queryProcessed pq = do
-  conn <- env modelStateConn
-  Model $ ReaderT (\_ -> DB.queryProcessed conn pq)
-
--- | Process a query for later use.
-processQuery :: (QueryParams q,QueryResults r) => Query -> q -> Model c s (ProcessedQuery r)
-processQuery template qs = do
-  Model $ ReaderT (\_ -> DB.processQuery template qs)
-
 -- | Query with some parameters.
-query :: (QueryParams ps,QueryResults r) => [String] -> ps -> Model c s [r]
+query :: (ToRow ps,FromRow r) => [String] -> ps -> Model c s [r]
 query q ps = do
   conn <- env modelStateConn
   Model $ ReaderT (\_ -> DB.query conn (fromString (unlines q)) ps)
 
 -- | Query a single field from a single result.
-single :: (QueryParams ps,QueryResults (Only r)) => [String] -> ps -> Model c s (Maybe r)
+single :: (ToRow ps,FromRow (Only r)) => [String] -> ps -> Model c s (Maybe r)
 single q ps = do
   rows <- query q ps
   case rows of
@@ -67,7 +56,7 @@
     _          -> return Nothing
 
 -- | Query a single field from a single result (no params).
-singleNoParams :: (QueryResults (Only r)) => [String] -> Model c s (Maybe r)
+singleNoParams :: (FromRow (Only r)) => [String] -> Model c s (Maybe r)
 singleNoParams q = do
   rows <- queryNoParams q
   case rows of
@@ -75,13 +64,53 @@
     _          -> return Nothing
 
 -- | Query with no parameters.
-queryNoParams :: (QueryResults r) => [String] -> Model c s [r]
+queryNoParams :: (FromRow r) => [String] -> Model c s [r]
 queryNoParams q = do
   conn <- env modelStateConn
   Model $ ReaderT (\_ -> DB.query_ conn (fromString (unlines q)))
 
 -- | Execute some SQL returning the rows affected.
-exec :: (QueryParams ps) => [String] -> ps -> Model c s Integer
+exec :: (ToRow ps) => [String] -> ps -> Model c s Int64
 exec q ps = do
   conn <- env modelStateConn
   Model $ ReaderT (\_ -> DB.execute conn (fromString (unlines q)) ps)
+
+-- | Create a new connection pool.
+newPool :: MonadIO m
+        => ConnectInfo -- ^ Connect info.
+        -> m Pool
+newPool info = liftIO $ do
+  var <- newMVar $ PoolState {
+    poolConnections = []
+  , poolConnectInfo = info
+  }
+  return $ Pool var
+
+-- | Connect using the connection pool.
+pconnect :: MonadIO m => Pool -> m Connection
+pconnect (Pool var) = liftIO $ do
+  modifyMVar var $ \state@PoolState{..} -> do
+    case poolConnections of
+      []           -> do conn <- connect poolConnectInfo
+                         return (state,conn)
+      (conn:conns) -> return (state { poolConnections = conns },conn)
+
+-- | Restore a connection to the pool.
+restore :: MonadIO m => Pool -> Connection -> m ()
+restore (Pool var) conn = liftIO $ do
+  modifyMVar_ var $ \state -> do
+    return state { poolConnections = conn : poolConnections state }
+
+-- | Use the connection pool.
+withPoolConnection :: (MonadCatchIO m,MonadIO m) => Pool -> (Connection -> m a) -> m ()
+withPoolConnection pool m = do
+  _ <- E.bracket (pconnect pool) (restore pool) m
+  return ()
+
+-- | A connection pool.
+data PoolState = PoolState {
+    poolConnections :: [Connection]
+  , poolConnectInfo :: ConnectInfo
+  }
+
+newtype Pool = Pool { unPool :: MVar PoolState }
