Handling request errors in snap with haskell I'm currently taking a look at Snap with Haskell. I'm pretty new to Haskell and functional programming in general and am working out the best way to handle errors. Currently I have something like this

createReport :: Handler b Api ()
createReport = do
report &lt;- readRequestBody 65536
modifyResponse $ setHeader "Content-Type" "application/json"
case (decode report :: Maybe Report) of
  Just x -&gt; do
    execute "INSERT INTO \"Reports\" (name) VALUES (?)" [name x]
    -- Do some other stuff and return a response
  Nothing -&gt; badRequest

badRequest :: Handler b Api()
badRequest = do
 modifyResponse . setResponseCode $ 400
 writeLBS "Bad Request"


I'm really wondering if this is the best way to go about errors or is there a better way to handle errors, specifically within Snap. Also any other comments you may have would be great.

Full code here https://github.com/elliotstokes/report-generator
What's "bad" about the request?

Giving a generic failure response isn't terribly useful to the poor soul who has to deal with handling the errors when interfacing with your API.  Why did it fail?  You're only catching the case where the response either is or isn't valid JSON, but what about other kinds of failure?

One thing you might want to look at is Digestive Functors, if you haven't already.  You can use it to do some of your error handling (eg. the value for FieldB is out of the specified range, etc.).  There's a Digestive Functors Aeson library that will let you send your JSON result through the form.  I don't do much with passing around JSON data myself, so this might be a bit more than you need.

The database can fail, too

You've only handled the case where decoding your JSON request can fail, not the database portion.  When it comes to the database, there are 4 basic outcomes to a query:


Success: Results
Success: No results
Failure: Constraint Violations (errors caused by the user)
Failure: SQL errors (errors caused by the developer)


So for queries that can fail (any time you run an insert/update/delete query), you'll want to make sure you're catching the Constraint Violations.  PostgreSQL Simple has a convenient function in Database.PostgreSQL.Simple.Errors (catchViolation) specifically for that (though if you're using the PostgreSQL Simple snaplet rather than using PostgreSQL Simple directly, you'll have to roll your own because the type signatures are incompatible).

Snap Extras might be right for you

There's another package called snap-extras that contains useful functions like reading the response as JSON or responding with JSON.  I know some people aren't too fond of it since it has a lot of dependencies, but I tend to use some of the other modules in it, so it's convenient for me.

If not, you can easily copy/paste just the parts you need into your own project.