I have a type that represents a persisted record. I want to have a very similar type that represents data that should be POSTed to create a new record.
This is the full type:
data Record = Reading
{ id: UUID
, value: String
...
}
the "new" type is the same minus the "id", which will be auto-generated by the db. How can I define this type? I am using servant to define the API.
My current strategy is to prefix the type and all fields with "new", which works but is reduntant for many-field models. I have also seen the nested strategy where I have a common shared type. I've also thought about making the id optional, but I really don't want to be able to post it.
You can implement this with a Higher Kinded Data like approach.
First, some imports:
Then, define a record with a higher kinded type parameter:
The
IdentityFunctor gives you a variant on this record which always has an Id.Using
Maybegives you a variant with an optional id.Proxycan be used as a Functor with a single uninteresting "unit" value. (and no values of the wrapped type). This lets us create a type for aRecordwith no ID.We can derive
Showfor ourRecord.Passing
omitNothingFields = TrueandallowOmitedFields = Truein theAesoninstances is required to parse aRecordWithoutIdas you'd expect. This does require a version of Aeson >= 2.2.0.0 (which as of writing is more recent than the latest Stackage Snapshot). You could probably implement the Aeson instances by hand if this version bound doesn't work for you.Encoding a value with an ID:
Encoding a value without an ID:
Decoding a value without an ID