Skip to main content

Routing Serdes

To route from a Kafka header, or reject records before any key/value deserialization, see Pre-deserialization filtering and routing.

Dekaf.Serialization.Routing routes heterogeneous records without allocating during steady-state dispatch. Registration is a cold-start operation; call Freeze() before passing a router to a producer or consumer. Frozen routers are safe for concurrent use.

dotnet add package Dekaf.Serialization.Routing

Route consumed values by topic

using Dekaf.Serialization.Routing;

var deserializer = new TopicRoutingDeserializer<IEvent>()
.Register("orders", orderDeserializer)
.Register("payments", paymentDeserializer)
.Freeze();

await using var consumer = await Kafka.CreateConsumer<string, IEvent>()
.WithBootstrapServers("localhost:9092")
.WithGroupId("event-router")
.WithValueDeserializer(deserializer)
.BuildAsync();

Each registered IDeserializer<TDerived> remains strongly typed. No cast delegate or closure is created while consuming.

Route Schema Registry payloads by schema ID

SchemaIdRoutingDeserializer<TBase> reads the schema ID from the standard Confluent framing: magic byte 0, followed by a four-byte big-endian schema ID. The selected deserializer receives the complete framed payload, so existing Schema Registry deserializers continue to work.

using Dekaf.Serialization.Routing;

var deserializer = new SchemaIdRoutingDeserializer<IEvent>()
.Register(17, customerV1Deserializer)
.Register(23, customerV2Deserializer)
.Freeze();

Malformed framing throws SerializationException. An unknown topic or schema ID also throws unless a fallback was registered with SetFallback(...) before Freeze().

Route produced values

Use TopicRoutingSerializer<TBase> when the destination topic determines the format. Use TypeRoutingSerializer<TBase> when the value's exact runtime type determines it.

using Dekaf.Serialization.Routing;

var serializer = new TypeRoutingSerializer<IEvent>()
.Register(orderSerializer)
.Register(paymentSerializer)
.Freeze();

Topic routes validate that the value matches the registered derived serializer type. Runtime-type routes use exact type matching; derived subclasses need their own registration or a fallback.

Inspect the raw record key

During value deserialization, SerializationContext.KeyData exposes the raw key bytes without a copy. SerializationContext.IsKeyNull distinguishes a Kafka null key from a non-null empty key.

The memory can reference a pooled receive buffer. Read it only during the current deserializer call; never store it, return it, or use it after deserialization completes. KeyData is not meaningful during serialization or key deserialization.