I am running into a problem setting up a simple proof of concept servant API. This is my User datatype and my API type:
data User = User { id :: Int, first_name :: String, last_name :: String } deriving (Eq, Show, Generic)
instance FromRow User
instance ToRow User
$(deriveJSON defaultOptions ''User)
type API = "users" :> ReqBody '[JSON] User :> Post '[JSON] User
The handler method for this uses postgresql-simple like this:
create u = liftIO $ head <$> returning connection "insert into users (first_name, last_name) values (?,?) returning id" [(first_name u, last_name u)]
Boilerplate code such as connecting to the db and the routing method has been elided. The problem is that if I make a POST request, what I want to be doing is creating a new user, so I would supply the JSON:
{ "first_name": "jeff", "last_name": "lebowski" }
but then my program fails at runtime with
Error in $: When parsing the record User of type Lib.User the key id was not present.
This makes sense because the API specified a User which has an id field. But I do not want to have to pass a bogus id in the request (since they are assigned by postgres sequentially) because that is gross. I also can't move the id field out of the User datatype because then postgres-simple fails because of a model-database mismatch when making a GET request to a different endpoint (which does the obvious thing: get by id. Not included above). What do I do here? Write a custom FromJson instance? I already tried setting the Data.Aeson.TH options flag omitNothingFields to True and setting the id field to be a Maybe Int, but that didn't work either. Any advice would be appreciated.