Skip to main content

Kafka Reference

Authentication options and data type handling for Kafka connections.

Server

servers:
- server: production
type: kafka
host: abc-12345.eu-central-1.aws.confluent.cloud:9092
topic: orders
format: json # or avro

Authentication

VariableExampleDescription
DATACONTRACT_KAFKA_SASL_USERNAMExxxThe SASL username (key)
DATACONTRACT_KAFKA_SASL_PASSWORDxxxThe SASL password (secret)
DATACONTRACT_KAFKA_SASL_MECHANISMPLAINDefault PLAIN; also SCRAM-SHA-256, SCRAM-SHA-512

If no username/password is set, the CLI connects without authentication (e.g. a local broker). host, topic, and format come from the contract's servers block. A username and password switch the connection to SASL_SSL.

Reading the topic

VariableExampleDescription
DATACONTRACT_KAFKA_MAX_MESSAGES100000Stop after this many messages. Unset by default: the whole topic is read
DATACONTRACT_KAFKA_TIMEOUT30Seconds to wait for a message before giving up. Default 30; the timer resets whenever a message arrives, so a slow read is never cut short
DATACONTRACT_KAFKA_GROUP_PREFIXmy-team-Replaces the default datacontract-cli- consumer group prefix. A UUID is still appended so each run gets a unique group ID. Useful when the service account's ACLs restrict which consumer group prefixes it may use

Every partition is read from its earliest offset up to the latest offset at the time the run starts, so messages produced while the checks are running are not included. Offsets are never committed, and each run uses its own consumer group, so testing a topic does not disturb a real consumer. Messages with no value (compaction tombstones) are skipped.

All messages are held in memory. DATACONTRACT_KAFKA_MAX_MESSAGES bounds that on a large topic, at the cost of checking a sample rather than the whole topic — the run reports when it has done so.

Schema Registry

For format: avro, the messages have to be decoded with the exact Avro schema they were written with — Avro is positionally encoded, so a schema that merely looks similar decodes to garbage. Topics written through the Confluent Schema Registry carry the id of that schema in a 5-byte prefix on every message. Point the CLI at the registry so it resolves the id, instead of falling back to the schema derived from the data contract:

VariableExampleDescription
DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URLhttps://psrc-12345.eu-central-1.aws.confluent.cloudThe schema registry base URL
DATACONTRACT_KAFKA_SCHEMA_REGISTRY_USERNAMExxxThe registry API key (optional)
DATACONTRACT_KAFKA_SCHEMA_REGISTRY_PASSWORDxxxThe registry API secret (optional)

Messages without the prefix (plain Avro) are decoded with the schema derived from the data contract, so a topic that mixes both still works, as does a topic whose schema evolved across several registry ids.

Data types

Importing

For Avro-encoded topics, import the schema with datacontract import avro. The Avro type is kept as physicalType:

Avro typelogicalType
stringstring
int, longinteger
float, doublenumber
booleanboolean
recordobject with nested properties
arrayarray
mapobject (values not expanded)
enumstring (symbols in the avroSymbols custom property)
bytes, fixedarray

Avro logical type annotations take precedence: decimalnumber (with precision/scale), datedate, uuid/duration/time-millis/time-microsstring. Unions must be [null, T] and make the field optional.

Testing

For format: json and format: avro, no type checks are generated — violations surface as decode errors or as value-check failures from logicalTypeOptions.

JSON messages are decoded as the types the contract declares; a message that is not a JSON object becomes a row of nulls and is reported as missing values. Avro messages are decoded with the schema they were written with — from the schema registry when the message carries a schema id, otherwise the one derived from the contract — and keep that schema's types:

Avro typeRead as
booleanBOOLEAN
int, longINTEGER, BIGINT
float, doubleFLOAT, DOUBLE
string, enumVARCHAR
bytes, fixedBLOB
recordSTRUCT
arrayLIST
mapMAP(VARCHAR, …)
decimal (on bytes/fixed)DECIMAL(precision, scale)
dateDATE
time-millis, time-microsTIME
timestamp-millis, timestamp-microsTIMESTAMP WITH TIME ZONE
local-timestamp-millis, local-timestamp-microsTIMESTAMP

A union must be [null, T]: which type of a wider union a message carries is known only per message, and a column has one type. Such a union is rejected with an error rather than guessed at.