Avro Schemas and Types

An Avro 129 schema is a JSON document; later chapters reuse this one:

order.avsc: the BookNest order as an Avro recordJSON
{
  "type": "record", "name": "Order", "namespace": "com.example.booknest",
  "doc": "One BookNest sample order (version 1)",
  "fields": [
    {"name": "order_id", "type": "long"},
    {"name": "customer_id", "type": "int"},
    {"name": "order_ts", "type": {"type": "long", "logicalType": "timestamp-millis"}},
    {"name": "channel",
     "type": {"type": "enum", "name": "Channel", "symbols": ["ios", "android", "web"]}},
    {"name": "status", "type": "string"},
    {"name": "currency", "type": "string"},
    {"name": "items", "type": {"type": "array", "items": {
      "type": "record", "name": "Item", "fields": [
        {"name": "book_id", "type": "int"},
        {"name": "qty", "type": "int"},
        {"name": "unit_price", "type": {"type": "bytes", "logicalType": "decimal",
                                        "precision": 7, "scale": 2}}]}}},
    {"name": "coupon", "type": ["null", "string"], "default": null},
    {"name": "discount",
     "type": {"type": "bytes", "logicalType": "decimal", "precision": 9, "scale": 2}},
    {"name": "total",
     "type": {"type": "bytes", "logicalType": "decimal", "precision": 9, "scale": 2}}
  ]
}

Avro has eight primitive types, six complex ones (record, enum, array, map, union, fixed) and logical types such as decimal, uuid, date and timestamps in millis, micros or nanos. A union lists branches: ["null", "string"] is a nullable string, and because a default must match the first branch, null comes first. A logical type annotates a primitive: timestamp-millis is a long of epoch milliseconds, and decimal stores an unscaled integer in bytes, so 40.20 travels as 4020 with scale 2, which cures the binary-float money of Numbers and Precision. A reader that ignores a logical type sees the underlying primitive.