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" } } } ```