I want to handle a database exception inside a servant handler monad.
I've tried to use the try
function from the Control.Exception
package to be able to case match with Left exception -> throwError err422 { errBody = ... }
.
I'm using postgresql-typed to interface with a PostgreSQL database. I want to capture a PGError exception.
The relevant code with the following modification:
accountHandler :: CreateAccountPayload -> Handler Account
accountHandler payload =
let errors = validateCreateAccountPayload payload in
if hasErrors errors then
throwError err422 { errBody = JSON.encode errors }
else
do
result <- try (liftIO (insertAccount payload))
case result of
Right account -> return account
Left exception -> throwError err422 { errBody = JSON.encode [ValidationError (Text.pack "email") (Text.pack "is already taken")] }
I am expecting to be able to capture a result from the database call and be able to case match it. The cases should be for an exception or a value. I am currently getting the following compile error:
src/Main.hs:64:17: error:
• Couldn't match type ‘IO’ with ‘Handler’
Expected type: Handler (Either e0 Account)
Actual type: IO (Either e0 Account)
• In a stmt of a 'do' block:
result <- try (liftIO (insertAccount payload))
In the expression:
do result <- try (liftIO (insertAccount payload))
case result of
Right account -> return account
Left exception -> throwError err422 {errBody = encode ...}
In the expression:
if hasErrors errors then
throwError err422 {errBody = encode errors}
else
do result <- try (liftIO (insertAccount payload))
case result of
Right account -> return account
Left exception -> throwError ...
|
64 | result <- try (liftIO (insertAccount payload))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I'd think that liftIO
lifts your IO Account
result into the Handler
monad. If so, perhaps try
should go first and liftIO
second:
result <- liftIO (try (insertAccount payload))
case result of
Right account -> return account
Left (PGError e) -> throwError err422