High Level API
The high level API relies on two mechanisms to provide type safety and reduce boilerplate code:
- Automatic ZIO Schema derivation for case classes
- Semi automatic generation of
ProjectionExpression's for case classes and sealed traits
ZIO Schema derivation​
The High Level API methods rely on a ZIO Schema for a particular type being in implicit scope. This is achieved using the DeriveSchema.gen macro. Internally codecs are automatically generated for the case classes based on the meta data provided by the Schema's.
final case class Person(id: String, name: String)
object Person {
implicit val schema: Schema.CaseClass2[String, String, Person] = DeriveSchema.gen[Person]
}
Projection Expression generation​
The automated generation of ProjectionExpression's is achieved using the ProjectionExpression.accessors method. For classes this method generates a ProjectionExpression for each field. For sealed traits it generates a ProjectionExpression for each child.
final case class Person(id: String, name: String)
object Person {
implicit val schema // ...
val (id, name) = ProjectionExpression.accessors[Person]
}
Using a ProjectionExpression as a springboard to creating further expressions​
In the above example Person.id and Person.name are ProjectionExpressions automatically generated by the ProjectionExpression.accessors method.
They are used as a springboard for creating further type safe APIs eg
Person.id === "1"creates aConditionExpressionPerson.name.set("Smith")creates anUpdateExpressionPerson.id.partitionKey === "1"creates a primary key expression
DynamoDBQuery CRUD methods​
There are also type safe query creation methods in the DynamoDBQuery companion object such as get, put, update, deleteFrom, queryAll and all these take expressions as arguments. So taking our example further we can see how all these APIs can be used together to create a type safe CRUD queries:
final case class Person(id: String, name: String)
object Person {
implicit val schema: Schema.CaseClass2[String, String, Person] = DeriveSchema.gen[Person]
val (id, name) = ProjectionExpression.accessors[Person]
}
val table = "person-table"
val person = Person("1", "John")
for {
_ <- DynamoDBQuery.put(table, person).where(!Person.id.exists).execute
found <- DynamoDBQuery.get(table)(Person.id.partitionKey === "1").execute.absolve
_ <- DynamoDBQuery.update(table)(Person.id.partitionKey === "1")(
Person.name.set("Smith")
).execute
_ <- ZIO.debug(found == person) // true
} yield ()