Skip to main content
Version: 2.x

Creating Models

The High Level API provides automatic serialization and deserialization of Scala case classes to and from DynamoDB types. This is done by requiring that an implicit ZIO Schema instance is in scope for the case class. This schema instance is generated semi-automatically by using the ZIO Schema DeriveSchema.gen[A] - placing this in the companion object of the case class ensures that this implicit is automatically in scope.

final case class Person(email: String, hobbies: Map[String, List[String]], registrationDate: Instant)
object Person {
implicit val schema: Schema.CaseClass3[String, Map[String, List[String]], Instant, Person] =
DeriveSchema.gen[Person]
}

This semi-automatically derived schema is used to automatically generate codecs for the case class (in the Codecs object) to perform the serialization and deserialization to and from DynamoDB types.

All standard Scala types are supported by the codecs, as well as nested case classes and collections. Note that where possible Scala types are mapped to corresponding DynamoDB types, for example Scala Map's and Set's are mapped to native DynamoDB types.

Scala Scalar TypesNative DynamoDB TypeNotes
UnitNULL
StringS
Numeric TypesN
Collection[byte]BAny Scala collection type of byte is serialized to a DynamoDB binary type
BooleanBOOL
java.time.*SThere is no native date/time support. Instant is serialized to a string in ISO-8601 format
java.util.UUIDSThere is no native UUID support
java.util.CurrencySThere is no native Currency support

Note in the below table that types A, K and V can be collections or case classes as well as scalar types.

Scala Collection TypesNative DynamoDB TypeNotes
Option[A]Some/None are represented by the presence and the absence of the field in the DynamoDB item respectively.
List[A]L
Set[String]SSNote that native Set types (SS, NS, BS) do not allow empty values - they are represented by the absence of the field in the DynamoDB item. However ZIO DynamoDB will automatically convert these missing values to empty Scala Sets.
Set of numeric typeNS
Set of binary typeBS
Set[A] of other typeLIf type is not a string or a numeric then a list is used
Map[String, A]Mif key type is a string then a native Map is used
Map[K, V]Lotherwise a list of tuple of key value pair is used

Scala Either Type encoding

The default encoding for the Scala Either type is an AttributeValue Map with a single key of Left or Right and the value being the encoded value eg for Either[String, Int], a Left value would be:

  "someField" : {
"Left" : {
"S" : "Error"
}
},

If you wanted a flatter "union" type encoding this can be achieved by specifying a transformation from a Schema.Fallback in implicit scope eg adding the following to the companion object of the case class:

implicit def fallbackEither[A, B](implicit schemaA: Schema[A], schemaB: Schema[B]): Schema[Either[A, B]] =
Schema.Fallback(schemaA, schemaB).transform(_.toEither, Fallback.fromEither)

Please see the integration suite TypeSafeApiCrudSpec.scala for an example.

Visualising DynamoDB Encodings

Note during model development you can use the optional zio-dynamodb-json module to view the DynamoDB types generated by the codecs. eg for the above example:

import zio.dynamodb.json._
val person = Person("email", Map("sports" -> List("cricket", "football")), Instant.now)
println(person.toJsonStringPretty[Person])

...would print the following which is representation of the native DynamoDB types and data in a standard JSON format used in AWS console views:

{
"registrationDate" : {
"S" : "2024-12-05T05:47:46.300286Z"
},
"hobbies" : {
"sports" : {
"L" : [
{
"S" : "cricket"
},
{
"S" : "football"
}
]
}
},
"email" : {
"S" : "email"
}
}

For more information about using the zio-dynamodb-json module please see the ZIO DynamoDB Json reference page.

For more information about customising data mappings please see the Codec Customization guide.