lilydocs
HTTP APIWebSocketConsumer
Message handling3 min read

Message contracts

Define queue, schema version, content kind, and canonical envelope headers.

A JSON body alone is not sufficient to select the correct handler. Lily uses the physical queue together with schema version and content kind. Producers and Consumers share this contract explicitly.

Handler selection#

Rust
#[queue("orders.created", version = 1, content = "json")]

This marker accepts messages from orders.created with version 1 and content json. Version describes the application payload contract, rather than the framework package release. Content kind is the publisher's declared contract, not an inferred body type. The framework performs neither JSON sniffing nor automatic upcasting.

Canonical envelope headers#

Header Contract Purpose
x-lily-event-id Non-nil, lowercase, hyphenated canonical UUID string Preserve event identity through retry and redelivery
x-lily-schema-version Canonical decimal string from 1–65535 Select a version; 01 is not canonical
x-lily-content-kind 1–64 bytes, [a-z0-9][a-z0-9._+-]* Exact content contract
x-retry-count Defaults to 0 when absent; otherwise AMQP LongInt, 0–100 Framework retry count

The first three headers are required. Producers written in other languages must preserve the canonical AMQP string representation as well: an arbitrary integer header type for schema version does not satisfy the string parser. An AMQP content_type such as application/json is useful metadata, but does not replace x-lily-content-kind.

The typed publisher produces encoding, MIME type, and Lily headers together. publish_json, publish_text, and publish_binary use their corresponding built-in content tokens. For an application decoder, choose another valid token through CustomPublishContent.

What happens to incompatible messages?#

The envelope is first checked for bounds and canonical representation. A missing version for that queue yields the permanent error QUEUE_SCHEMA_VERSION_UNSUPPORTED; an existing version with no matching content yields QUEUE_CONTENT_KIND_UNSUPPORTED. This selection occurs before opening a scope or entering application middleware.

Payload decoding can still fail after contract selection. For example, malformed JSON is rejected permanently by Json<T> even when the header says json. Middleware cannot observe every poison message; operational snapshots and dead letters address different observation needs.

Identity, retries, and version transitions#

Preserve the event ID when resending the same event. The broker's redelivered flag, framework RetryCount, and an application's business attempt identity are distinct concepts. None alone makes every external side effect exactly-once; transactional inbox defines the database deduplication boundary.

For a new version, first make Consumer replicas accept both old and new tuples, then update publishers. Competing consumers on one physical queue are not partitioned by version. See versioning and rollouts for details.

Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code