I am writing a HTTP REST API and I want strongly typed model classes in Scala e.g. if I have a car model Car
, I want to create the following RESTful /car
API:
1) For POST
s (create a new car):
case class Car(manufacturer: String,
name: String,
year: Int)
2) For PUT
s (edit existing car) and GET
s, I want tag along an id
too:
case class Car(id: Long,
manufacturer: String,
name: String,
year: Int)
3) For PATCH
es (partial edit existing car), I want this partial object:
case class Car(id: Long,
manufacturer: Option[String],
name: Option[String],
year: Option[Int])
But keeping 3 models for essentially the same thing is redundant and error prone (e.g. if I edit one model, I have to remember to edit the other models).
Is there a typesafe way to maintain all 3 models? I am okay with answers that use macros too.
I did manage to combine the first two ones as following
trait Id {
val id: Long
}
type PersistedCar = Car with Id
I would go with something like that
trait Update[T] {
def patch(obj: T): T
}
case class Car(manufacturer: String, name: String, year: Int)
case class CarUpdate(manufacturer: Option[String],
name: Option[String],
year: Option[Int]) extends Update[Car] {
override def patch(car: Car): Car = Car(
manufacturer.getOrElse(car.manufacturer),
name.getOrElse(car.name),
year.getOrElse(car.year)
)
}
sealed trait Request
case class Post[T](obj: T) extends Request
case class Put[T](id: Long, obj: T) extends Request
case class Patch[T, U <: Update[T]](patch: U) extends Request
With Post & Put everything is straightforward. With Patch a bit more complicated. I'm pretty sure CarUpdate class can be replaced with auto generated with macros.
If you'll update you Car model, you'll definitely will not forget about patch, because it will fail at compile time. However this two models looks too "copy-paste-like".
You could represent your models as Shapeless records, then the id
is simply one more field on the front, and the mapping to/from options can be done generically using ordinary Shapeless type-level programming techniques. It should also be possible to generically serialize/deserialize such things to JSON (I have done this in the past, but the relevant code belongs to a previous employer). But you would definitely be pushing the boundaries and doing complex type-level programming; I don't think mature library solutions with this approach exist yet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With