EDIT: For those with similar illnesses, I have found that this is due to the “Extensible Entries” problem, which I will personally research more.
EDIT2: I started to solve this (a few weeks later), being pretty explicit regarding data types and having several data types per semantic data unit. For example, if the database contains X , my code has XAction to represent the objects I want to do with X and XResponse to relay X to the http client. And then I need to create supporting code for dragging bits between instances. Not perfect, but I like that it is explicit, and I hope that when my models crystallize, in fact it should not be very dependent and should be very reliable.
I am not sure what is the right level of abstraction to solve this problem (e.g. records? Or Yesod?). So I just lay out a simple case.
Simple Case / TL; DR
I want to decode the request body into type
data Comment = Comment {userid :: ..., comment :: ...}
but I don’t really want the request body to contain userid , the server will supply it based on its Auth headers (or wherever I would like to get the default data, fill in the field).
So they really pass me something like:
data SimpleComment = SimpleComment {comment :: ...} deriving (Generic, FromJSON)
And I turn it into Comment . But simultaneously saving both almost identical types is a hassle, not DRY.
How to solve this problem?
Problem Details
I have a post type:
data Comment = Comment {userid :: ..., comment :: ...}
I have a POST route:
postCommentR :: Handler Value postCommentR = do c <- requireJsonBody :: (Handler Comment) insertedComment <- runDB ... returnJson insertedComment
Note that the route requires the user to supply their userid (in the Comment type, which is at least redundant because their identifier is associated with their header headers. In the worst case, this means that I need to check that users add their own identifier or discard their provided identifier, in which case why do they supply it in the first case.
So, I need a post type that Comment minus userid , but I don't know how to do this wisely.
My current (awful but working) solution
So, I created my own type with a derived FromJSON (for the request body), which is almost completely redundant with the Comment type.
data SimpleComment = SimpleComment {comment :: ...} deriving (Generic, FromJSON)
Then my new route should decode the request body according to this, and then combine a SimpleComment with the userid field to make it Comment :
postComment2R :: Handler Value postComment2R = do c <- requireJsonBody :: (Handler SimpleComment) (uid, _) requireAuthPair insertedComment <- runDB $ insertEntity (Comment { commentUserid = uid , commentComment = comment c}) returnJson ...
Talk about the template. And my use case is more complex than this simple Comment type.
If it does, you might be able to say I'm using Yesod Scaffolding .