Skip to content

Consume a Kafka topic with a contract

Validate every message off a Kafka topic, send valid records to your sink, and dead-letter the rest with a structured reason. Requires pip install pycharter[streaming].

import asyncio

from pycharter import Validator
from pycharter.streaming import (
    ContractStreamProcessor,
    HeaderContractResolver,
    IteratorSink,
    MemoryDLQSink,
)
from pycharter.streaming.kafka import KafkaSource


async def main() -> None:
    # 1. Where messages come from.
    source = KafkaSource(
        bootstrap_servers="kafka:9092",
        topic="orders.v1",
        group_id="orders-validator",
    )

    # 2. How to find the right contract per message. HeaderContractResolver
    #    reads the contract id and version from message headers; pair with
    #    StaticContractResolver if your producers don't set headers.
    resolver = HeaderContractResolver(
        contract_loader=lambda ref: Validator.from_file(
            f"contracts/{ref.name}/{ref.version}.yaml"
        )
    )

    # 3. Where valid messages go, and where bad ones land.
    valid_sink = IteratorSink()
    dlq = MemoryDLQSink()

    processor = ContractStreamProcessor(
        source=source,
        valid_sink=valid_sink,
        dlq=dlq,
        resolver=resolver,
    )

    await processor.run()  # runs until cancelled


asyncio.run(main())

MemoryDLQSink is for testing — swap it for a Kafka, file, or S3-backed DLQ sink in production. The DLQReason enum (DECODE_ERROR, VALIDATION_FAILED, UNKNOWN_CONTRACT, …) is attached to every dead-lettered message so you can route or alert on it.

Streaming is experimental through the 0.x series — the public API is unlikely to shift but minor breaking changes may land between minor releases. See Streaming Validation for the deep dive on config, circuit-breaker tuning, and replay.

See also: Validate a CSV in 30 seconds, Emit a quality score to Prometheus.