Best practice for validating JSON requests with Yesod

haskell, json, rest, yesod

Solution

There is a digestive-functors-aeson library that utilizes `Text.Digestive.Form` and allows to write expressive validation rules. Here is a gist from its tests:

pokeForm :: Monad m => Form Text m Pokemon
pokeForm = Pokemon <$> "name" .: nonEmptyText
                   <*> "number" .: parseInteger
  where
    nonEmptyText = check "Name cannot be empty" (not . T.null) $
                     text Nothing

testPokedexFailHead =
    let (v, r) = runIdentity $ digestJSON pokedexForm json
    in testGroup "Submit pokedex with a single invalid item"
         [ testCase "Failed validation" $ r @?= Nothing
         , testCase "jsonErrors shows correct errors" $ jsonErrors v @?= errors
         ]
  where
    (Just json) = decode "{\"pokemon\":[{\"name\":\"\"}]}"
    (Just errors) = decode "{\"pokemon\":[{\"name\":\"Name cannot be empty\"}]}"

As you can see the library can produces pretty verbose errors which are labeled with field names which failed validation.

Problem

I am writing JSON RESTFul service with Yesod and need to implement requests validation. I cannot use Yesod.Form for it because of the service accepts JSON. I like the way scalaz validation uses but I didn't find any approach like this in Haskell. Is there a best practice to implement validation which would allow to answer with structured errors messages, something like this: Request ``` { "birthDate": "2017.07.14", "count": "three", "kind": "baz", "entity": { "id": -1 } } ``` Response ``` { "errors": { "birthDate": "Date should be less than 2014.05.25", // current date "count": "should be a number", "kind": "must be one of [foo, bar]", "entity": { "id": "Entity with id -1 not found" } } } ```

Original source