Skip to content

Avro schemas in AsyncAPI

AsyncAPI describes a message payload with JSON Schema by default. To use an Avro schema instead, wrap it in a Multi Format Schema Object: a schemaFormat that says “this is Avro” and a schema with the Avro definition. You can write the Avro schema inline or point to your existing .avsc file.

In 3.0 the payload itself is the Multi Format Schema Object, so schemaFormat and schema sit under payload:

asyncapi: 3.0.0
info:
title: Users
version: 1.0.0
channels:
userSignedUp:
address: user.signedup
messages:
userSignedUp:
$ref: '#/components/messages/userSignedUp'
operations:
onUserSignedUp:
action: receive
channel:
$ref: '#/channels/userSignedUp'
components:
messages:
userSignedUp:
contentType: application/octet-stream
payload:
schemaFormat: application/vnd.apache.avro;version=1.9.0
schema:
type: record
name: UserSignedUp
namespace: com.example.users
fields:
- name: userId
type: string
- name: email
type: string
- name: signedUpAt
type:
type: long
logicalType: timestamp-millis

The Avro schema is written as YAML here; it is the same object as the JSON in an .avsc file.

Keep the Avro schema where your build already uses it and reference it:

components:
messages:
userSignedUp:
payload:
schemaFormat: application/vnd.apache.avro;version=1.9.0
schema:
$ref: './avro/user-signed-up.avsc'

The specification requires the referenced file to be in the same format as the schemaFormat that references it: an Avro schema can only reference Avro.

In 2.x schemaFormat belongs to the message, next to payload:

asyncapi: 2.6.0
info:
title: Users
version: 1.0.0
channels:
user.signedup:
subscribe:
message:
schemaFormat: application/vnd.apache.avro;version=1.9.0
contentType: application/octet-stream
payload:
type: record
name: UserSignedUp
namespace: com.example.users
fields:
- name: userId
type: string
- name: email
type: string

A payload: { $ref: './avro/user-signed-up.avsc' } works with the AsyncAPI parser, which resolves references before it validates. Plain JSON Schema validators reject it, because the 2.x specification schema expects the Avro schema itself there; the 3.0 schema allows a reference explicitly.

When you migrate to 3.0, move schemaFormat into the payload and the old payload under schema.

The value names the Avro specification version your schema follows:

Schema written as schemaFormat
Avro, JSON or YAML application/vnd.apache.avro;version=1.9.0
Avro in JSON application/vnd.apache.avro+json;version=1.9.0
Avro in YAML application/vnd.apache.avro+yaml;version=1.9.0

Versions 1.9.0 to 1.11.1 are listed in the Multi Format Schema page. Tools are not required to support Avro: the AsyncAPI parser handles it through @asyncapi/avro-schema-parser, which AsyncAPI CLI and Studio include.

  • schemaFormat says what language the schema is written in: Avro.
  • contentType (or the document’s defaultContentType) says how the bytes on the wire are encoded.

Avro data is usually binary, so a defaultContentType: application/json left over from a template is wrong for it. Avro binary has no single standard media type; application/octet-stream and avro/binary are both common. If producers really send Avro’s JSON encoding, application/json is right.

With Confluent Schema Registry or Apicurio, every message carries the ID of the schema that wrote it. The Kafka message binding says where:

components:
messages:
userSignedUp:
bindings:
kafka:
bindingVersion: 0.5.0
key:
type: string
description: User ID, used for partitioning
schemaIdLocation: payload
schemaIdPayloadEncoding: confluent
schemaLookupStrategy: TopicNameStrategy
payload:
schemaFormat: application/vnd.apache.avro;version=1.9.0
schema:
$ref: './avro/user-signed-up.avsc'
  • schemaIdLocation: payload (Confluent puts a magic byte and a 4-byte ID in front of the data) or header.
  • schemaIdPayloadEncoding: the vendor or number of bytes, for example confluent or apicurio-new.
  • schemaLookupStrategy: the subject naming strategy your serializer uses, such as Confluent’s default TopicNameStrategy.

The binding’s key is a schema too; it describes the record key in JSON Schema terms.

Questions

Why does validation say the payload "must match else schema"?

AsyncAPI 3.0 checks a payload with an if/then/else: if it has a schema property it is a Multi Format Schema, otherwise it must be a plain AsyncAPI Schema Object. An Avro record put straight under payload, or a 2.x-style schemaFormat on the message, lands in the else branch and fails. Wrap the Avro schema as payload: { schemaFormat: …, schema: … }.

Can one document mix Avro and JSON Schema payloads?

Yes. schemaFormat is set per payload, so each message can use its own format. Payloads without it are AsyncAPI Schema Objects.

Can I design Avro schemas in AsyncAPI Studio?

Studio renders and validates documents that use Avro payloads, because it includes the Avro parser. It has no Avro designer; write the .avsc in your editor and reference it.