diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Joe Canero (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Joe Canero nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/sqlite-simple-errors.cabal b/sqlite-simple-errors.cabal
new file mode 100644
--- /dev/null
+++ b/sqlite-simple-errors.cabal
@@ -0,0 +1,43 @@
+name:                sqlite-simple-errors
+version:             0.1.0.0
+synopsis:            Wrapper around errors from sqlite-simple
+description:         Wrapper around errors from sqlite-simple
+homepage:            https://github.com/caneroj1/sqlite-simple-errors
+license:             BSD3
+license-file:        LICENSE
+author:              Joe Canero
+maintainer:          jmc41493@gmail.com
+copyright:           Copyright: (c) 2016 Joe Canero
+category:            Database
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Database.SQLite.SimpleErrors
+                     , Database.SQLite.SimpleErrors.Parser
+                     , Database.SQLite.SimpleErrors.Types
+  build-depends:       base >= 4.7 && < 5
+                     , sqlite-simple
+                     , text
+                     , parsec
+  default-language:    Haskell2010
+
+test-suite sqlite-simple-errors-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  other-modules:       Utils
+                     , SQLUtils
+  main-is:             Spec.hs
+  build-depends:       base
+                     , sqlite-simple-errors
+                     , sqlite-simple
+                     , text
+                     , mtl
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/caneroj1/sqlite-simple-errors
diff --git a/src/Database/SQLite/SimpleErrors.hs b/src/Database/SQLite/SimpleErrors.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/SQLite/SimpleErrors.hs
@@ -0,0 +1,34 @@
+module Database.SQLite.SimpleErrors
+(
+  DatabaseResponse
+, runDBAction
+) where
+
+import Control.Exception
+import Database.SQLite.Simple (FormatError, ResultError, SQLError)
+import Database.SQLite.SimpleErrors.Types
+import Database.SQLite.SimpleErrors.Parser
+
+type DatabaseResponse a = Either SQLiteResponse a
+
+runDBAction :: IO a -> IO (DatabaseResponse a)
+runDBAction sqlAction = do
+  res <- try sqlAction
+  case res of
+    (Left e)  -> return . Left $ convertException e
+    (Right e) -> return $ Right e
+
+convertException :: SomeException -> SQLiteResponse
+convertException se = handleResultError se $ fromException se
+
+handleResultError :: SomeException -> Maybe ResultError -> SQLiteResponse
+handleResultError se Nothing = handleFormatError se (fromException se)
+handleResultError _ (Just e) = SQLResultError e
+
+handleFormatError :: SomeException -> Maybe FormatError -> SQLiteResponse
+handleFormatError se Nothing = handleSQLError se (fromException se)
+handleFormatError _ (Just e) = SQLFormatError e
+
+handleSQLError :: SomeException -> Maybe SQLError -> SQLiteResponse
+handleSQLError se Nothing = throw se
+handleSQLError _ (Just e) = receiveSQLError e
diff --git a/src/Database/SQLite/SimpleErrors/Parser.hs b/src/Database/SQLite/SimpleErrors/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/SQLite/SimpleErrors/Parser.hs
@@ -0,0 +1,42 @@
+module Database.SQLite.SimpleErrors.Parser where
+
+import Control.Monad
+import           Data.Text                (Text)
+import qualified Data.Text as Text hiding (Text)
+import Database.SQLite.Simple
+import Database.SQLite.SimpleErrors.Types
+import Text.Parsec
+import Text.Parsec.Text
+
+type SQLiteParser = Parser SQLiteResponse
+
+constraintNameOnly :: String -> Parser ()
+constraintNameOnly n = void $ string (n ++ " constraint failed")
+
+constraintName :: String -> Parser ()
+constraintName n = constraintNameOnly n >> char ':' >> spaces
+
+getRest :: Parser Text
+getRest = Text.pack <$> many1 anyChar
+
+parseConstraint :: String -> Constraint -> SQLiteParser
+parseConstraint n c = constraintName n >> SQLConstraintError c <$> getRest
+
+parseConstraintNoDetails :: String -> Constraint -> SQLiteParser
+parseConstraintNoDetails n c =
+  constraintNameOnly n >> return (SQLConstraintError c Text.empty)
+
+constraintParser :: Parsec Text () SQLiteResponse
+constraintParser =
+  parseConstraintNoDetails "FOREIGN KEY" ForeignKey <|>
+  parseConstraint "NOT NULL"    NotNull             <|>
+  parseConstraint "UNIQUE"      Unique              <|>
+  parseConstraint "CHECK"       Check
+
+parseError :: SQLError -> SQLiteResponse
+parseError e@SQLError{sqlErrorDetails = details} =
+  either (\_ -> SQLOtherError e) id $ parse constraintParser "" details
+
+receiveSQLError :: SQLError -> SQLiteResponse
+receiveSQLError e@SQLError{sqlError = ErrorConstraint} = parseError e
+receiveSQLError e = SQLOtherError e
diff --git a/src/Database/SQLite/SimpleErrors/Types.hs b/src/Database/SQLite/SimpleErrors/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/SQLite/SimpleErrors/Types.hs
@@ -0,0 +1,20 @@
+module Database.SQLite.SimpleErrors.Types where
+
+import Control.Exception
+import Data.Text (Text)
+import Data.Typeable
+import Database.SQLite.Simple (FormatError, ResultError, SQLError)
+
+data Constraint = NotNull
+                | ForeignKey
+                | Unique
+                | Check
+  deriving (Show, Eq)
+
+data SQLiteResponse = SQLConstraintError Constraint Text
+                    | SQLFormatError FormatError
+                    | SQLResultError ResultError
+                    | SQLOtherError  SQLError
+  deriving (Show, Eq, Typeable)
+
+instance Exception SQLiteResponse
diff --git a/test/SQLUtils.hs b/test/SQLUtils.hs
new file mode 100644
--- /dev/null
+++ b/test/SQLUtils.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module SQLUtils where
+
+import Database.SQLite.Simple
+import Database.SQLite.Simple.ToField
+import Data.Text (Text)
+
+createTableSQL :: Query
+createTableSQL =
+  "CREATE TABLE if not exists Test (name varchar(50) NOT NULL UNIQUE\
+  \                  ,age  int NOT NULL CHECK(age > 0)              \
+  \                  ,id   integer PRIMARY KEY NOT NULL);          "
+
+createNextTableSQL :: Query
+createNextTableSQL =
+  "CREATE TABLE if not exists Next (label varchar(10)               \
+  \                  ,test_id int NOT NULL                          \
+  \                  ,FOREIGN KEY(test_id) REFERENCES Test(id));"
+
+insertSQL :: Query
+insertSQL =
+  "INSERT INTO Test (name, age) VALUES (?, ?)"
+
+insertOtherSQL :: Query
+insertOtherSQL =
+  "INSERT INTO Next (label, test_id) VALUES (?, ?)"
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Data.Text (Text)
+import Database.SQLite.Simple
+import Database.SQLite.Simple.ToField
+import Database.SQLite.SimpleErrors
+import Database.SQLite.SimpleErrors.Types
+import SQLUtils
+import Utils
+
+main :: IO ()
+main = executeTestRunner [checkNotNullConstraintName,
+                          checkNotNullConstraintAge,
+                          checkUniqueConstraint,
+                          checkCheckConstraint,
+                          checkForeignKeyConstraint]
+
+checkNotNullConstraintName :: Test (DatabaseResponse ())
+checkNotNullConstraintName = Test "Not Null Constraint - Name"
+                                  (Left $ SQLConstraintError NotNull "Test.name")
+                                  test
+  where test conn = runDBAction $ execute conn insertSQL sqlData
+        sqlData :: (Maybe Text, Int)
+        sqlData = (Nothing, 10)
+
+checkNotNullConstraintAge :: Test (DatabaseResponse ())
+checkNotNullConstraintAge = Test "Not Null Constraint - Age"
+                                 (Left $ SQLConstraintError NotNull "Test.age")
+                                 test
+  where test conn = runDBAction $ execute conn insertSQL sqlData
+        sqlData :: (Text, Maybe Int)
+        sqlData = ("name", Nothing)
+
+checkUniqueConstraint :: Test (DatabaseResponse ())
+checkUniqueConstraint = Test "Unique Constraint - Name"
+                             (Left $ SQLConstraintError Unique "Test.name")
+                             test
+  where
+    test conn =
+      runDBAction (execute conn insertSQL sqlData) >>
+        runDBAction (execute conn insertSQL sqlData)
+    sqlData :: (Text, Int)
+    sqlData = ("name", 10)
+
+checkCheckConstraint :: Test (DatabaseResponse ())
+checkCheckConstraint = Test "Check Constraint - Age"
+                            (Left $ SQLConstraintError Check "Test")
+                            test
+  where
+    test conn = runDBAction $ execute conn insertSQL sqlData
+    sqlData :: (Text, Int)
+    sqlData = ("name", 0)
+
+checkForeignKeyConstraint :: Test (DatabaseResponse ())
+checkForeignKeyConstraint = Test "Check Foreign Key Constraint - TestID"
+                                 (Left $ SQLConstraintError ForeignKey "")
+                                 test
+  where
+    test conn = runDBAction $ execute conn insertOtherSQL sqlData
+    sqlData :: (Text, Int)
+    sqlData = ("other", 10)
diff --git a/test/Utils.hs b/test/Utils.hs
new file mode 100644
--- /dev/null
+++ b/test/Utils.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Utils
+(
+  Test(..)
+, executeTestRunner
+)where
+
+import Control.Monad.State
+import Database.SQLite.Simple (open, close, execute_, Connection)
+import SQLUtils
+
+data Test a = Test {
+  testName   :: String
+, expects    :: a
+, testAction :: Connection -> IO a
+}
+
+data Results = Results {
+  passed :: Int
+, failed :: Int
+}
+
+instance Show Results where
+  show Results{..} =
+    "\nResults"  ++
+    "\n-------"  ++
+    "\npassed: " ++ show passed ++ "/" ++ show total ++
+    "\nfailed: " ++ show failed ++ "/" ++ show total
+    where total = passed + failed
+
+empty :: Results
+empty = Results 0 0
+
+logResult :: Bool -> Results -> Results
+logResult True  results = results{passed = passed results + 1}
+logResult False results = results{failed = failed results + 1}
+
+newtype TestRunner a = TestRunner {
+  runSuite :: StateT Results IO a
+} deriving (Monad, Functor, Applicative, MonadState Results, MonadIO)
+
+executeTestRunner :: (Eq a, Show a) => [Test a] -> IO ()
+executeTestRunner tests = print =<< execStateT (runSuite $ runTests tests) empty
+
+setupDB :: Connection -> IO ()
+setupDB conn = execute_ conn createTableSQL     >>
+               execute_ conn createNextTableSQL >>
+               execute_ conn "PRAGMA foreign_keys = ON"
+
+runTests :: (Eq a, Show a) => [Test a] -> TestRunner ()
+runTests []           = liftIO $ putStrLn "Done with tests"
+runTests (test:tests) = do
+  conn <- liftIO $ open ":memory:"
+  liftIO $ setupDB conn
+  got  <- liftIO $ testAction test conn
+  liftIO $ close conn
+  logResults got test
+  runTests tests
+  where
+    logResults :: (Show a, Eq a) => a -> Test a -> TestRunner ()
+    logResults got Test{..} = do
+      liftIO $ do
+        putStrLn   "\n"
+        print      testName
+        unless passed $ do
+          putStr     "Expected: " >> print expects
+          putStr     "Got:      " >> print got
+        putStrLn   "----------"
+        putStrLn $ "Passed:   " ++ show passed
+      modify (logResult passed)
+      where
+        passed = expects == got
