Single Message Transforms (SMTs) in Kafka

Single Message Transforms (SMTs) in Kafka

A small, powerful idea inside Kafka Connect: tweak, trim, rename, route or drop each record while it travels between Kafka and the outside world. This guide explains what SMTs are, how they work, where they shine and where they should never be used.

Imagine a mailroom at a big company. Letters arrive from many senders, and each one has a different envelope, different handwriting and sometimes the wrong department name. A helpful clerk stands at the entrance with a stamp, a marker and a pair of scissors. She fixes the department name, blacks out a private phone number, stamps the date on the back, and drops junk mail into the bin. She handles every letter on its own, and she never needs to remember the last letter she saw.

That clerk is a good picture of a Single Message Transform, usually shortened to SMT. It is a tiny piece of logic attached to a data pipeline in Apache Kafka. Each record that passes through gets a quick, light edit before moving on. No new program is needed, and often no code is written at all, because you switch on ready-made transforms with a few lines of settings.

SMTs are one of the most practical tools in the Kafka world, and also one of the most misunderstood. People either overlook them and write extra programs to do simple edits, or they stretch them too far and end up with slow, fragile pipelines. By the end of this guide, you will know exactly what SMTs can do, what they cannot do, and how to use them safely in production.

1What Is a Single Message Transform?

The name is a full description. Read it slowly, one word at a time, and the whole idea falls into place.

A message in Kafka is one record: a small package holding a key, a value, a timestamp and optional labels called headers. A transform is a change applied to that package. The word single tells you the most important rule: the change looks at exactly one record at a time. It cannot peek at the record before it, wait for the record after it, or combine two records into one.

The core promise

An SMT takes one record in and hands back either one record or nothing. If it hands back one record, that record may be changed a lot or a little. If it hands back nothing, the record is dropped and disappears from the pipeline. That is the entire contract.

Everyday Analogy

Think of the passport control desk at an airport. The officer looks at one traveller at a time, stamps the passport, sometimes asks the traveller to step aside, and waves the next person forward. The officer does not compare travellers with each other or keep a running total. SMTs work the same way: one record, one quick decision, next please.

Where SMTs live

SMTs are part of Kafka Connect, the framework that moves data between Kafka and other systems such as databases, search engines and cloud storage. They are not part of plain producers or consumers, and they are not part of Kafka Streams. You attach them to a connector, and they run inside the Connect workers alongside it. They were added to Kafka in version 0.10.2, released in 2017, after users kept asking for a simple way to do small edits without writing whole programs.

1 in
record read by the transform each time
0 or 1 out
records handed to the next stage
0 lines
of code needed for built-in transforms

What people use them for

Reshape

Change the record’s shape

Rename fields, remove fields, flatten nested data or wrap a value inside a new field.

Protect

Hide sensitive data

Mask fields such as phone numbers or card numbers before they reach a wider audience.

Route

Pick the destination

Change the topic name so records land in the right place, for example one topic per day.

Enrich

Add small facts

Insert a constant field, the source topic name or a timestamp.

Convert

Fix types and formats

Turn text dates into real timestamps, or numbers into strings, so the target system accepts them.

Filter

Drop the unwanted

Remove records you do not need, such as test events or deletion markers.

i
Tip

If you can describe your edit as “for every record, do this small thing”, an SMT is probably the right tool. If your description needs the words “across records”, “so far” or “wait until”, you need something bigger.

Why SMTs were created

Before SMTs existed, small edits were a real annoyance. Suppose a database connector produced a field called “cust_nm” but every other team expected “customerName”. The only fix was to write a separate consumer that read every record, renamed the field and wrote everything back to a second topic. That doubled the traffic, added a service to run and monitor, and delayed the data.

The Kafka community noticed that most such edits were tiny and identical in spirit: rename, drop, mask, re-route. SMTs were added in Kafka 0.10.2 so that these chores could be done inside the connector itself, in the same thread, on the same record, with no second topic. The change turned an entire category of small services into a few lines of settings.

Everyday Analogy

Before SMTs, fixing a label meant sending every parcel to a separate repair shop across town and bringing it back. With SMTs, the repair happens at the loading dock, in a few seconds, as the parcel passes by. Nothing travels farther than it needs to.

The cost of the extra hop

Imagine a topic that carries ten thousand records per second. A separate cleaning service would read all ten thousand, write ten thousand to a second topic, and require a second copy of storage for the cleaned data. An SMT does the same edit in place and adds no extra topic, no extra copy and no extra service to keep alive.

2Kafka Connect in Five Minutes

SMTs only make sense inside Kafka Connect, so here is a short tour of the place where they live.

Kafka Connect is a ready-made framework for copying data into Kafka and out of Kafka. Instead of writing a custom program for every database or storage service, you install a connector, give it settings, and let the framework run it. Connect handles the boring parts: splitting work across machines, remembering how far it has read, restarting after failures and reporting its health.

Source and sink

Source

Outside world into Kafka

A source connector reads from something like a database or a file and writes records into Kafka topics.

Sink

Kafka out to the outside world

A sink connector reads records from Kafka topics and writes them to something like a search index or cloud storage.

The moving parts

1

Worker

A worker is a running Connect process. Several workers together form a cluster and share the load.

2

Connector

The connector is the configured job, for example “copy the orders table into Kafka”. It decides how to divide the work.

3

Task

Tasks do the real copying. A connector splits its job into tasks, and the tasks are spread over workers. The tasks.max setting caps how many.

4

Converter

A converter turns records into bytes for Kafka and bytes back into structured data. Common choices are JSON, Avro and Protobuf.

5

Transform

An optional SMT sits in the task’s path and edits each record on its way through.

Everyday Analogy

Kafka Connect is like a courier company. The workers are delivery vans, the connector is a standing delivery contract, the tasks are individual drivers, and the converter is the packing station that boxes items properly. An SMT is the label-checking desk that corrects a label just before a parcel goes into the van.

flowchart LR
    DB[("Source system: database")] --> SC["Source connector task"]
    SC --> K["Kafka topic"]
    K --> KC["Sink connector task"]
    KC --> ES[("Target system: search index")]
    
FIG 1 — Kafka Connect in one picture: a source task fills Kafka, a sink task empties it.
i
Note

Connect has been part of Apache Kafka since version 0.9. It is managed through a REST interface, so you create, inspect and change connectors by sending small web requests to a worker instead of editing files on many machines.

Standalone and distributed modes

Connect can run in two ways. In standalone mode, one process runs everything, connector settings are read from files given at startup, and its progress markers are kept in a local file. It suits a laptop, a test or a single-machine job. In distributed mode, several workers form a cluster, connector settings and progress are stored in special Kafka topics, and you manage connectors through the REST interface. If one worker fails, the others take over its tasks.

SMT settings work identically in both modes. The difference is only where the settings are stored and how you update them. Production systems almost always use distributed mode.

Progress markers and repeated records

Connect keeps track of how far each task has got. A source task periodically saves a marker, called an offset, describing the position in the outside system. A sink task commits how far it has read in the Kafka topic. If a task crashes, it restarts from its last saved marker and may re-handle a few records. By default this gives at-least-once delivery: nothing is lost, but a few records can appear twice. Newer Kafka versions also offer exactly-once support for source connectors, starting with version 3.3.

This matters for SMTs because a transform may see the same record twice. That is fine as long as the transform is a pure function of the record: run it twice and you get the same output. It is one more reason to avoid transforms that keep counters or send messages elsewhere.

Everyday Analogy

A reader who uses a bookmark can put the book down at any time. If the bookmark slips a few pages back, the reader re-reads a few pages and carries on. Re-reading harms nobody, provided the reading itself changes nothing. A transform that only reads and rewrites a record is like that; one that also rings a bell each time is not.

3Where SMTs Sit in the Pipeline

The exact position of an SMT decides what data it sees. Getting this right removes a lot of confusion.

On the way in: source connectors

A source connector task first reads data from the outside system and builds a record. Before that record is turned into bytes and written to Kafka, the chain of SMTs runs on it. The converter comes after the transforms. So an SMT working on a source connector sees a structured record, not bytes.

flowchart LR
    SYS[("External system")] --> ST["Source task builds a record"]
    ST --> T1["SMT 1"]
    T1 --> T2["SMT 2"]
    T2 --> CV["Converter: record to bytes"]
    CV --> TP["Kafka topic"]
    
FIG 2 — Source side: transforms run before the converter.

On the way out: sink connectors

A sink connector task reads bytes from Kafka. The converter first turns those bytes into a structured record. Only then do the SMTs run, and finally the cleaned-up record is handed to the sink connector to write into the target system. On the sink side the converter comes before the transforms.

flowchart LR
    TP["Kafka topic"] --> CV["Converter: bytes to record"]
    CV --> T1["SMT 1"]
    T1 --> T2["SMT 2"]
    T2 --> SK["Sink task writes to target"]
    SK --> TS[("Target system")]
    
FIG 3 — Sink side: transforms run after the converter.
Everyday Analogy

Picture a translator at a border. Leaving your country, you have your documents corrected first, and then a clerk seals them in an envelope for travel. Arriving in another country, the envelope is opened first, and only then are the documents corrected for local rules. In both directions the correction happens while the documents are open, and the envelope is the converter.

Why this order matters in practice

  • On the source side, the transform edits the record as the connector understands it, before any Kafka format is applied. If the connector produces structured data, the transform can name fields directly.
  • On the sink side, the transform sees data as Kafka stored it, after decoding. If Kafka holds plain JSON without a schema, the transform sees loose maps instead of a strict structure. That difference affects which transforms work well, as chapter 10 explains.
  • The same SMT can be reused on both sides, but its results can differ because the incoming shapes differ.
!
Watch Out

An SMT changes only the copy of the record flowing through this connector. It never rewrites data already stored in Kafka. If two connectors read the same topic, each applies its own transforms independently, and the topic itself stays untouched.

What a transform can actually see

A record inside Connect carries more than a key and a value. Knowing every part helps you decide which transform to use.

Part of the recordWhat it holdsCan a transform change it?
TopicThe destination (source) or origin (sink) topic nameYes, this is how routing works
PartitionWhich partition the record belongs to, if knownYes, in custom transforms
Key and key schemaThe label used for partitioning and orderingYes, with key transforms
Value and value schemaThe main payload of the recordYes, with value transforms
TimestampWhen the event happened or was storedYes, in custom transforms
HeadersSmall labels attached to the recordYes, with header transforms
Everyday Analogy

A parcel has a destination address (topic), a shelf number (partition), a customer tag (key), contents (value), a postmark (timestamp) and small stickers such as “fragile” (headers). A clerk can change any of these, but each change has different consequences. Changing a sticker is harmless. Changing the destination sends the parcel somewhere else entirely.

One useful detail: on a source connector, the record has not yet been written to Kafka, so it has no offset. On a sink connector, the record was already read from Kafka, so its topic, partition and offset are all known. This is why an “insert the offset” option makes sense only on the sink side.

i
Tip

When a transform behaves oddly, first list which parts of the record it can see at that position. Half of all mysteries end there.

4The “Single Message” Rule and What It Means

Every strength and every limit of SMTs comes from one rule: they look at one record at a time.

What that allows

Because each record is handled alone, SMTs need no memory. They have no counters, no lookups of earlier records and no waiting rooms. That makes them extremely cheap: a transform is little more than a small function that runs in a fraction of a millisecond. It also makes them safe to run in parallel and safe to repeat. If a task restarts and replays a few records, running the same transform again gives the same result.

What that forbids

The same rule rules out a whole family of jobs. An SMT cannot count records, add up amounts, work out an average or detect that three events happened close together. It cannot join two topics. It cannot sort records, hold one back until another arrives, or split one record into several. It cannot call out to a slow web service for every record without harming the entire pipeline.

JobFits an SMT?Why
Rename a fieldYesOnly needs the current record
Mask a phone numberYesOnly needs the current record
Drop test eventsYesDecision uses only this record
Send records to a daily topicYesUses the record’s own timestamp
Count orders per customerNoNeeds memory across records
Join orders with customer profilesNoNeeds a second data source and stored state
Split an order into line itemsNoOne record cannot become many
Remove duplicatesNoNeeds to remember earlier records
Everyday Analogy

A car wash tunnel scrubs each car as it rolls through, and does the same job for every car. It cannot tell you how many red cars passed today, or notice that the same car came through twice. Answering those questions needs a person with a clipboard, which is a different kind of tool.

The connection to stateless processing

In stream processing, the word stateless describes work that needs no memory between events. SMTs are stateless by design. If you have read about stateless and stateful operations in Kafka Streams, you can think of SMTs as the stateless corner of the picture: filtering, mapping and re-labelling. Anything stateful, such as counting, windowing and joining, belongs in Kafka Streams, ksqlDB or a stream processor, not in an SMT.

One record in, zero or one record out. Everything else is someone else’s job.

Order is preserved

Because an SMT handles records one by one and returns one record for each, the relative order of surviving records inside a partition stays the same. Record A still comes before record B. This is a valuable property. It is exactly what you lose when you use tools that reshuffle data, and it is why SMTs are safe to place on pipelines where ordering matters, such as change events from a database.

The only ways an SMT can disturb ordering are by changing the key (so later records may go to a different partition), by changing the topic name (so records flow into a different topic), or by dropping some records. Everything else is order-safe.

i
Note

Kafka guarantees order only within a partition, never across partitions. SMTs do not change that rule. They simply avoid making it worse.

5How You Configure SMTs

Using a built-in SMT means adding a few settings to a connector. No programming is required.

The four ideas you need

1

List your transforms

The connector has a setting named transforms. Its value is a comma-separated list of short nicknames you invent, called aliases, such as one for masking and one for routing.

2

Say what each alias is

For each alias you set a type, which names the transform to use, for example the one that masks fields.

3

Give it its settings

Each transform has its own options, written with the alias as a prefix, such as which fields to mask.

4

Mind the order

The transforms run in the exact order written in the list. The output of the first becomes the input of the second.

Everyday Analogy

Setting up SMTs is like writing a recipe card. First you list the steps by name (chop, season, bake). Then, for each step, you say exactly what it means (chop the onions finely). The cook follows the card top to bottom, and swapping two steps can change the dish completely.

Key versions and value versions

Every Kafka record has a key and a value, and most built-in transforms come in two flavours: one that works on the key and one that works on the value. The names make this plain, with the word Key or Value at the end. Choosing the wrong flavour is a very common beginner mistake, and the symptom is usually “nothing changed” or “the wrong part changed”.

Where the settings live

SMT settings belong to a single connector. They are not global. Two connectors on the same cluster can use completely different transforms, or none at all. Changing a connector’s settings and updating it through the REST interface restarts its tasks with the new chain, while other connectors keep running.

Checking your settings before going live

Connect offers a validation call that checks a connector’s settings without starting it. It reports missing options, misspelled names and wrong types. Using it in your deployment pipeline catches simple mistakes early, long before a bad chain touches production data.

i
Tip

Give aliases meaningful names such as maskPhone or routeByDay, not t1 and t2. When something goes wrong at night, a readable alias in the logs tells you which step failed.

i
Note

Only transforms installed on the workers can be used. The built-in ones ship with Apache Kafka. Additional ones from projects such as Debezium or from your own team must be placed on each worker’s plugin path before they can be referenced.

A small glossary to keep nearby

TermPlain meaning
RecordOne message in Kafka, made of a key, a value, a timestamp and optional headers
ConnectorA configured job that copies data between Kafka and another system
TaskA worker unit that does the actual copying for a connector
ConverterThe part that turns records into bytes and back again
AliasThe short nickname you give a transform inside the settings
PredicateA yes-or-no test that decides whether a transform runs for a record
TombstoneA record with a key and no value, meaning the key was deleted
Dead letter topicA separate topic that collects records a sink could not process

6A Tour of Built-in SMTs: Shaping Fields

Apache Kafka ships with a set of ready-made transforms. This chapter covers the ones that change what is inside a record’s value or key.

The field-shaping toolbox

InsertField

Add a field

Adds a fixed value, or details about the record itself such as its topic, partition, timestamp or, on sinks, its offset. Useful for stamping where data came from.

ReplaceField

Keep, drop or rename

Lets you include only certain fields, exclude others, or rename fields to match the target system’s naming.

MaskField

Hide sensitive data

Replaces chosen fields with an empty or zero value of the same type, or with a replacement you provide in newer versions.

Cast

Change basic types

Turns simple values into another simple type, such as a number into text, or text into a number.

Flatten

Unnest data

Turns nested structures into one flat level, joining names with a separator such as a dot. Many databases prefer flat rows.

HoistField

Wrap a value

Wraps a plain value inside a structure with a single named field. Handy when a target needs a named column.

ExtractField

Pull one field out

Does the opposite of hoisting: replaces the whole value or key with just one field taken from inside it.

SetSchemaMetadata

Label the schema

Sets the name or version of the record’s schema, which matters when a schema registry or a target table uses those names.

Everyday Analogy

Think of a form-processing office. One clerk adds a “received on” stamp (InsertField). Another crosses out irrelevant boxes and renames a heading (ReplaceField). A third covers the social security number with a black bar (MaskField). Someone flattens a folded page so it fits the scanner (Flatten). Each clerk touches one page at a time, and none of them needs to know what is on any other page.

A closer look at masking

Masking is among the most common uses of SMTs, because it lets a pipeline hide private data before it leaves Kafka. The transform swaps the chosen field with a harmless placeholder. Be clear about what it is: masking hides a value; it does not protect it with a secret key. Once masked, the original is gone from that copy of the record, which is exactly what you want, but it is not encryption and it cannot be reversed.

A closer look at flatten and hoist

Nested data is common in modern applications. An order might hold an address block, which holds a city and a postcode. Many targets, such as relational tables, cannot store nested blocks directly. Flatten turns the block into separate top-level fields with names like the address block, a separator and the city. Hoisting is used on the other side: a bare number or text value cannot become a column without a name, so hoisting gives it one.

Why built-ins are attractive

  • Maintained with Kafka itself, so they are widely tested.
  • No code to write, build or deploy.
  • Documented behaviour, consistent across connectors.
  • Fast, because each one does a very small job.

Where they fall short

  • Each one does one narrow thing, so complex edits need long chains.
  • Cast handles only simple types, not whole nested objects.
  • They rely on records having a predictable shape.
  • They cannot look anything up or use outside knowledge.
i
Tip

Prefer removing what you do not need over masking it. A field that is dropped early can never leak later, while a masked field still shows up as an empty column that someone might misread.

Worked examples in plain words

Numbers and names make the ideas concrete. Imagine a customer record that arrives with a nested address, a phone number and a spelling nobody likes. Here is what each transform does to it.

TransformRecord beforeRecord after
InsertFieldCustomer 42, city PuneCustomer 42, city Pune, plus a field naming the source system
ReplaceField (rename)Field called cust_nmField called customerName
ReplaceField (exclude)Fields: id, name, internal_notesFields: id, name
MaskFieldPhone 98xxxxxx01Phone field replaced by an empty text
CastAge stored as the text “34”Age stored as the whole number 34
FlattenAddress block holding city and postcodeTwo top-level fields: address.city and address.postcode
HoistFieldPlain value 100A structure with one field, for example amount, holding 100
ExtractFieldStructure with id and nameOnly the id value
Everyday Analogy

Reading this table is like looking at before-and-after photos of a room makeover. Each row is a single small change: a new sticker on the door, a repainted wall, a covered window. No single change rebuilds the room, but a handful in the right order transforms it.

!
Watch Out

Flatten changes field names. A masking or renaming step that expects the old nested name will not find it afterwards, so place Flatten carefully in the chain and refer to the flattened names in later steps.

7Built-in SMTs for Keys, Routing, Time and Headers

Some transforms do not touch the data fields much. They change where a record goes, how it is labelled, or how its time is written.

Keys and partitioning

The key of a record decides which partition it lands in, and the partition decides ordering. Sometimes a source produces records with no useful key. ValueToKey copies chosen fields from the value into the key, and ExtractField can then reduce that new key to a single plain value. Together they let you give every record a key, such as a customer identifier, so that related records stay together and in order.

Everyday Analogy

A key is like the tab colour on a filing folder. If every folder for the same customer has the same tab colour, they end up in the same drawer and stay in the order they were filed. Adding a key is choosing the tab colour before the folder goes into the cabinet.

Routing to other topics

RegexRouter

Rename by pattern

Rewrites the destination topic name using a pattern. Example: remove a long database prefix, or add an environment suffix.

TimestampRouter

One topic per period

Adds the record’s date to the topic name, giving names like orders-20260929. Perfect for daily indexes or daily storage folders.

On a source connector, routing changes which Kafka topic the record is written to. On a sink connector, it changes which topic name the sink believes the record came from, which many sinks use to choose a table, an index or a folder name. That second effect is the reason routing SMTs are so popular for sinks.

Time formats

TimestampConverter converts a time value between several forms: a number counting milliseconds since 1970, a text string in a chosen pattern, and proper timestamp, date or time types. Different systems disagree on how to write time, and this SMT is the translator that makes them agree.

Headers

Headers are small labels attached to a record without changing its value. Built-in transforms can copy or move a field into a header, insert a fixed header, or drop headers you no longer want. Headers are useful for routing hints, tracing identifiers and source tags, because downstream tools can read them without opening the value.

GoalTypical transformExample result
Give records a useful keyValueToKey, then ExtractFieldKey becomes the customer id
Shorten topic namesRegexRouterLong prefix removed from the topic
Create daily destinationsTimestampRouterTopic name ends with the date
Fix time formatsTimestampConverterText date becomes a real timestamp
Carry tags with the recordHeader transformsSource name stored as a header
!
Watch Out

Changing the key of a record changes its future partition if it is written back to Kafka. Existing records with the old key stay where they are, so ordering guarantees can be broken across old and new data. Decide on your key strategy before the first record flows.

8Filtering and Predicates: Applying an SMT Only Sometimes

Real pipelines are rarely uniform. Some records need a transform, and others must be left alone. Predicates make that possible.

The Filter transform

The built-in Filter transform drops records. By itself, it is a blunt tool: it removes every record that reaches it. That sounds useless until you learn that it is designed to be paired with a predicate, a yes-or-no test that decides whether a transform should run for a given record.

Predicates

Predicates were added to Kafka Connect in version 2.6. You declare a predicate with an alias, choose its type, and then tell a transform to run only when the predicate is true, or only when it is false by turning on a “negate” switch.

TopicNameMatches

Match by topic

True when the record’s topic name matches a pattern. Example: mask fields only for topics that start with customers.

HasHeaderKey

Match by header

True when the record carries a header with a given name. Example: drop anything marked as a test record.

RecordIsTombstone

Match deletion markers

True when the record’s value is empty, meaning “this key was deleted”. Example: skip tombstones for a target that cannot handle them.

Everyday Analogy

Consider a supermarket checkout with an “age check” step. The clerk asks for identification only when the basket contains alcohol. The check is the transform, and “basket contains alcohol” is the predicate. Everyone else walks through without delay. With the negate switch, the rule flips: check everyone who does not have a loyalty card.

Common uses

  • Drop test traffic: filter out records that carry a “test” header, so demo data never reaches the warehouse.
  • Handle deletions: remove tombstones for a sink that cannot delete rows, or keep them only for a sink that can.
  • Apply masking selectively: mask personal fields only on topics that contain personal data.
  • Route only some records: apply a topic rename to matching topics and leave the rest alone.
ANTI-PATTERN 01 Avoid
What people do

They add the Filter transform to a connector without attaching any predicate.

Why it hurts

With no test attached, every record is dropped. The connector looks healthy, its tasks are running, and yet nothing arrives at the destination. It can take hours to notice.

What to do instead

Always pair Filter with a predicate, and after any change, verify that the expected number of records reaches the target.

A worked scenario with predicates

Consider a sink connector that reads three topics: customers, orders and heartbeats. The team wants personal fields masked on customers, tombstones dropped everywhere because the target cannot delete rows, and heartbeat records ignored completely. A tidy design uses three small rules instead of one confusing one.

1

Drop heartbeats

A filter transform paired with a predicate that matches the heartbeat topic name. Only heartbeat records are removed.

2

Drop tombstones

Another filter paired with the tombstone predicate. Deletion markers never reach the target.

3

Mask customer fields

A masking transform paired with a predicate that matches the customers topic. Orders are untouched.

Everyday Analogy

Picture a mail sorter with three coloured stickers. Red means “throw away”, yellow means “black out the phone number”, and no sticker means “let it pass”. Each sticker is a predicate, and each action is a transform. The sorter never has to think, and each rule is easy to explain to a new colleague.

Why this design is good

  • Each rule has one purpose and a readable name.
  • Changing the masking rule cannot affect the heartbeat rule.
  • New topics that match none of the predicates pass through unchanged.

Where it can go wrong

  • A topic renamed by an earlier step may no longer match its predicate.
  • A predicate with a loose pattern can match more topics than intended.
  • Forgetting the negate switch inverts a rule and does the opposite of what you meant.

9Chaining Transforms: Order Is Everything

A single SMT does one small thing. Real work comes from lining several of them up, and the order you choose changes the result.

How a chain flows

The first transform receives the record built by the connector. Its output goes to the second transform, and so on down the list. If any transform returns nothing, the record is dropped and the later transforms never see it. Whatever comes out of the last transform is what the converter or the sink receives.

flowchart TB
    IN["Record from connector"] --> A["Step 1: unwrap the change envelope"]
    A --> B{"Step 2: is it a test record?"}
    B -- "yes" --> D["Dropped"]
    B -- "no" --> C["Step 3: mask personal fields"]
    C --> E["Step 4: rename the topic by date"]
    E --> OUT["Record continues to converter or sink"]
    
FIG 4 — A four-step chain: unwrap, filter, mask, route.

A worked example in words

Suppose a database connector produces change events. Each event wraps the useful row inside an envelope that also holds the previous version of the row and some bookkeeping. A sensible chain looks like this.

1

Unwrap first

Take the current row out of the envelope. Later steps can then refer to plain field names.

2

Filter early

Drop records nobody wants, so later steps do less work.

3

Mask or remove private fields

Do this before the record can travel any further.

4

Route last

Change the topic name at the end, because earlier steps, especially predicates that test the topic name, rely on the original.

Everyday Analogy

Getting dressed follows a fixed order too. Socks go on before shoes, and the coat goes on last. If you reverse the order, you can still get dressed, but the result looks wrong and may not even work. Transform chains have exactly the same kind of natural order.

Keeping chains short

Every extra step adds a little work to every record and one more thing that can break. Long chains are also hard to read. If you find yourself writing more than four or five steps, stop and ask whether the source data could be cleaner, whether a different connector setting does the job, or whether the logic belongs in a stream processor.

i
Tip

Test a chain with a small sample topic before pointing it at production data. Send five or six hand-made records, including one odd one, and inspect what comes out.

Three chain designs to practise with

Designing a chain gets easier with practice. Here are three small scenarios, each with the order you would choose and the reason behind it.

ScenarioChain, in orderWhy this order
Send a customers table to a warehouse without phone numbersUnwrap change envelope, then exclude phone and email fields, then rename topicFields must be plain before they can be removed, and the topic is renamed last so earlier steps still see the original name
Write website click events to one storage folder per dayConvert text time to a real timestamp, then route by timestampThe router needs a proper timestamp to build the date in the name
Give keyless file records a customer keyCopy customer id into the key, then extract the single valueThe extraction step needs the key to hold a structure first
Everyday Analogy

Assembling flat-pack furniture follows the same rule. You cannot attach the doors before the frame stands, and you cannot polish the top after it is glued to the wall. Instruction sheets exist because the order of small steps decides whether the wardrobe stands or collapses.

Reading a chain backwards

A useful debugging trick is to work backwards. Start from the record you expect at the end and ask, “what did the last step need to see?” Then ask the same about the step before it. When you find the step where the real record differs from what the next step expects, you have found the fault. This is much faster than staring at the whole chain at once.

10Schemas and Data Shapes: Why Transforms Sometimes Surprise You

Most SMT trouble comes from a mismatch between the shape of the data and the shape the transform expects.

Two kinds of data

Inside Connect, a record’s value can arrive in two forms. With a schema, the data is a structured object with named, typed fields, and Connect knows that “age” is a whole number and “name” is text. Without a schema, the data is a loose map of names and values, and Connect can only guess at the types. Avro and Protobuf converters usually give schemas. Plain JSON often does not.

AspectWith a schemaWithout a schema
Field typesKnown and enforcedGuessed from the data
Type changes with CastPredictableSometimes limited or surprising
Adding new fieldsType must be declaredEasy, but easy to get wrong
Target compatibilityBetter, because types are clearDepends on the target’s guesses
Everyday Analogy

A schema is like a printed form with labelled boxes for name, age and city. A schemaless record is like a plain sticky note where someone scribbled details in any order. A clerk can process a form quickly, but a sticky note needs guesswork, and a strangely written one leads to mistakes.

Common surprises

  • Missing fields: a transform that names a field which some records do not have may fail or skip. Records are not always uniform.
  • Null values: a field can be present and empty. Some transforms handle this gracefully, and others raise an error.
  • Deletion markers: a tombstone has a key but no value. Transforms that assume a value exist can fail on it. Guard with a predicate.
  • Nested versus flat: a field name that works on a flat record does not exist on a nested one until it has been flattened.
  • Schema changes: when the source adds a field, the schema changes and some transforms react. Test what happens after an upgrade.
!
Watch Out

A transform that works perfectly on today’s data may fail when the source system adds, renames or removes a field. Treat SMT settings as code that depends on the source’s data format, and review them whenever that format changes.

Nulls and tombstones in detail

Two special cases trip up more pipelines than any others. A null field is a field that exists but holds no value, such as a customer with no middle name. A tombstone is a whole record whose value is empty, used in Kafka to say “the thing with this key has been deleted”. Log-compacted topics rely on tombstones to clean up old data, and database connectors emit them after a row is deleted.

Many transforms assume the value is present. When a tombstone arrives, such a transform may fail, pass it through unchanged or produce an odd result. Meanwhile, a sink that cannot delete rows may choke on the marker. There are three sensible strategies.

Keep

Pass tombstones through

Right when the target can delete rows and you want deletions to propagate.

Drop

Filter them out

Right when the target cannot cope with deletion markers or deletions should be ignored.

Guard

Skip risky transforms

Use a predicate so transforms that need a value do not run on tombstones, while the marker still travels on.

Everyday Analogy

A tombstone is like a note that says “remove this person from the guest list”. A clerk who tries to check the guest’s dietary needs on that note will be confused, because there is no guest to check. The clerk needs a rule: if the note is a removal, act on the list, not on the guest’s details.

!
Watch Out

Always test one tombstone and one record with a null field before releasing a chain. They are the two most frequent causes of surprise failures a few days after go-live, when the first deletion happens.

11Popular Real-World Recipes

These patterns appear in many production Kafka Connect setups. Each one is small, yet each removes a whole extra service.

Debezium: unwrapping database change events

Debezium, an open-source change-data-capture project led by Red Hat, reads database logs and publishes every row change to Kafka. Each event arrives wrapped in an envelope holding the row before, the row after and details about the source. Its ExtractNewRecordState transform removes the envelope and leaves just the new row, so a sink connector can write it straight into a table or an index. It also has options for how to treat deletions.

Debezium: the outbox pattern

Applications often need to save data and announce an event in one safe step. In the outbox pattern, the application writes the event into a special table inside the same database transaction, and Debezium publishes it. Debezium’s EventRouter transform then sends each event to the right topic based on a column, and puts the payload and identifiers where downstream consumers expect them. It turns a plain table into a clean stream of business events.

Sending data to Elasticsearch or Amazon S3 by day

Search indexes and object storage are often organized by date, so old data can be deleted cheaply. Using TimestampRouter on a sink connector, records land in a destination named after their date, such as one index per day. The team never has to write a program to sort records into days.

Protecting personal data on its way to a warehouse

An analytics team wants customer data in a cloud warehouse but must not receive phone numbers or national identifiers. A sink connector removes some fields and masks others before the warehouse ever sees them. Because the transform runs in the sink, the original topic stays intact for teams that are allowed to see everything.

Confluent’s managed connectors

Confluent, the company founded by Kafka’s creators, offers managed connectors in its cloud service, and many of them let you attach SMTs through settings in the same way. Teams that run no Connect servers of their own still use the same transform ideas.

Keys for ordered processing

A file-based source produces records with no key. By copying an order identifier from the value into the key with ValueToKey and ExtractField, every record of one order lands in the same partition, and consumers see the order’s events in sequence.

The best SMT is the one that lets you delete a whole service.

12Writing Your Own SMT

When no built-in transform fits, Kafka lets you build your own. It is easier than most people expect, and easier to get wrong than most people expect.

When a custom SMT makes sense

Build your own only after checking three things. First, ask whether a chain of built-in transforms can do the job. Second, ask whether a change to the source or the sink could avoid the need. Third, ask whether the logic really is single-record and stateless. If all three answers point to “a small custom rule”, then a custom SMT is a good choice. Typical examples are converting a company-specific code format, applying a house naming standard, or changing a deeply nested payload in a way no built-in handles.

What a custom SMT is

A custom SMT is a small Java class that follows a public contract called Transformation. The contract has a handful of parts:

1

Configure

Receives the settings you wrote in the connector and prepares itself, for example reading which field to change.

2

Apply

Receives one record and returns a new record, or nothing to drop it. This is where the real work happens.

3

Describe its settings

Declares the options it accepts, their types and defaults, so that validation and documentation work.

4

Close

Releases anything it opened when the task stops.

Built-in transforms usually have a Key version and a Value version of the same logic, sharing one core. Following that convention in your own code makes your transform feel familiar to every operator.

Everyday Analogy

Building a custom SMT is like making a custom jig in a workshop. A jig is a small guide that holds a piece the same way every time so a cut is always identical. It works wonderfully because it does one job, with no thinking involved. If the jig starts needing memory, moods or decisions, it has become a different machine.

Getting it onto the workers

  • Package the class, with only the libraries it needs, into a JAR file.
  • Copy it into a folder listed in each worker’s plugin path. Connect loads each plugin in its own isolated space, so its libraries cannot clash with others.
  • Restart the workers so they discover it, then reference it by its full class name in the connector settings.
  • Keep versions in step across all workers. A cluster where half the machines have the old plugin behaves unpredictably.

Good habits for custom code

Do

  • Keep it pure: the same input always gives the same output.
  • Handle missing fields, nulls and tombstones on purpose.
  • Cache anything expensive, such as a rebuilt schema, and reuse it.
  • Write unit tests with hand-made records, including odd ones.
  • Version the JAR and record which connectors use it.

Do not

  • Call a database or web service for every record.
  • Keep growing lists or counters inside the class.
  • Change the meaning of a field without telling consumers.
  • Depend on record order across partitions.
  • Swallow errors silently and return the record unchanged.
!
Watch Out

A custom SMT runs inside your Connect workers. A bug that crashes or hangs it can stall every task on the machine. Test it as carefully as you would test any production service.

A design walk-through for a small custom SMT

To make the idea concrete, imagine a company where phone numbers arrive in twelve different styles, some with spaces, some with dashes, some with country codes and some without. The sink needs one clean style. No built-in transform does this, so the team decides to build one. They approach it in a calm, repeatable way.

1

Write the rule in one sentence

“Take the phone field, remove spaces and dashes, and add the country code if missing.” If the sentence needs the word “and” more than twice, split it into two transforms.

2

Decide the settings

Which field name to change and which country code to add. These become options so the same class works on many topics.

3

Handle both data shapes

Support records with a schema and records without one, or say clearly that only one is supported.

4

List the odd cases

Field missing, field empty, field not text, tombstone, extremely long value. Decide the behaviour for each and write it down.

5

Write the tests first

Create hand-made records for every case in the list and check each output before touching production.

6

Release like any service

Version the JAR, deploy to a test cluster, compare counts, and roll out gradually.

Everyday Analogy

This is how a tailor makes a simple alteration. First decide exactly what to change, then measure, then handle awkward fabric, then do a trial fitting before cutting for real. Skipping the trial fitting is how a shirt ends up one sleeve short.

i
Tip

Before building, search the plugin ecosystem. Projects such as Debezium and many connector vendors already publish transforms for common jobs, and a reviewed, maintained plugin beats a private one.

13Error Handling: When a Record Breaks a Transform

Sooner or later, one strange record will arrive that a transform cannot handle. What happens next is a choice you make in advance.

Two behaviours

The setting errors.tolerance decides how Connect reacts when a record fails during conversion or transformation.

none (default)

Fail fast

The task stops at the first bad record. Nothing is skipped silently. It is strict and safe, but one bad record can halt a whole pipeline.

all

Skip and continue

Bad records are skipped, and the task carries on. The pipeline keeps flowing, but you must make sure skipped records are noticed.

The safety nets

  • Retries: for failures that may pass with time, Connect can retry for a set period before giving up.
  • Logging: failures can be written to the worker log. Turning on message content in logs helps debugging but may reveal private data, so use it with care.
  • Dead letter queue: for sink connectors, failed records can be sent to a separate Kafka topic so nothing is lost. Extra headers can explain why each record failed, including the stage and the error message.
flowchart TB
    R["Record enters converter and transforms"] --> OK{"Did it succeed?"}
    OK -- "yes" --> NEXT["Record continues to the sink"]
    OK -- "no" --> TOL{"errors.tolerance"}
    TOL -- "none" --> STOP["Task stops with an error"]
    TOL -- "all" --> DLQ{"Dead letter queue set?"}
    DLQ -- "yes" --> SEND["Record and error details go to the dead letter topic"]
    DLQ -- "no" --> SKIP["Record is skipped and logged"]
    
FIG 5 — What Connect does when a transform or converter fails.
Everyday Analogy

A sorting office can handle a damaged parcel in three ways. The strictest rule halts the whole belt until someone deals with it. A relaxed rule tosses the parcel aside and keeps the belt moving. The best rule puts the parcel into a labelled “problem” tray with a note saying what went wrong, so a person can inspect it later. The dead letter queue is that tray.

What is not covered

These settings cover failures in converting and transforming records. They do not cover every kind of problem. If the sink cannot reach its target system, or the source cannot connect to its database, that is a different class of failure, and it follows the connector’s own retry rules. Also, dead letter queues exist only for sink connectors. For source connectors, failures are logged or skipped but not saved to a topic.

i
Tip

A sensible production default is to skip bad records and keep the pipeline alive, but only together with a dead letter queue, an alert on its size and a person who owns emptying it. Tolerating errors without watching them just hides data loss.

Reading a dead letter record

A record in the dead letter topic keeps its original key and value, so you can see exactly what arrived. When context headers are switched on, Connect also attaches labels that describe the failure: the connector name, the task, the stage where it failed (for example a transformation), the class name of the step that raised the error, and the error message. This makes the dead letter topic a self-explaining inbox rather than a mystery pile.

A healthy routine has four parts. Someone is alerted when the topic grows. Someone reads a few records and finds the pattern. The cause is fixed in the source, the settings or the transform. Finally, the saved records are replayed into the pipeline once the fix is live, so no data is lost for good.

1

Alert

The size of the dead letter topic passes a limit, and a person is notified.

2

Diagnose

Read a handful of failed records and their headers to find what they have in common.

3

Fix

Correct the transform, the settings or the upstream data.

4

Replay

Send the saved records through the pipeline again and confirm they reach the target.

!
Watch Out

A dead letter topic that nobody reads is just a slow way to lose data. Assign an owner and a review schedule on the day you create it.

14SMTs vs Kafka Streams, ksqlDB and Consumer Code

SMTs are one of four ways to change data around Kafka. Choosing the right one saves a lot of effort.

ToolBest atCan remember earlier records?Effort
SMTLight edits while data enters or leaves Kafka through ConnectNoSettings only, or a small class
Kafka StreamsRich processing inside your own Java or Kotlin serviceYes, with state storesA full application
ksqlDBSQL-style filtering, joining and aggregating on streamsYesQueries on a ksqlDB cluster
Consumer codeAnything at all, with full controlOnly if you build itHighest, all yours
flowchart TB
    Q1{"Is the change small and per record?"} -- "no" --> Q2{"Does it need counting, joining or windows?"}
    Q1 -- "yes" --> Q3{"Is data moving through Kafka Connect?"}
    Q3 -- "yes" --> SMT["Use an SMT"]
    Q3 -- "no" --> Q4["Use a stateless step in Streams or a simple consumer"]
    Q2 -- "yes" --> Q5["Use Kafka Streams or ksqlDB"]
    Q2 -- "no" --> Q6["Use consumer code or a custom service"]
    
FIG 6 — A quick way to choose between SMTs and heavier tools.
Everyday Analogy

SMTs are a pocket screwdriver, handy for quick small jobs right where you stand. Kafka Streams is a full workshop, where you can build almost anything but must set it up first. ksqlDB is a workshop that takes orders in plain sentences. Consumer code is raw metal and a blank blueprint. Nobody assembles a whole cabinet with a pocket screwdriver, and nobody opens a workshop to tighten one screw.

The dividing questions

1

Does the job need memory?

If yes, an SMT is out immediately. Move to Kafka Streams or ksqlDB.

2

Does one input make several outputs?

An SMT can return only one record. Splitting needs another tool.

3

Are you already using Connect?

If yes and the change is light, an SMT costs almost nothing. If no, adding Connect only for a transform is rarely worth it.

4

Should other consumers see the changed data?

An SMT on a sink changes only what that sink writes. If every consumer needs the cleaned data, write it to a new topic using a stream processor or a source-side SMT.

i
Note

The tools combine well. A common design is a source connector with light SMTs that unwrap and mask, a Kafka Streams service that counts and joins, and a sink connector with a few SMTs that route by date.

Signs that you have outgrown SMTs

Most pipelines start with one or two harmless transforms. Over months, requests pile up. Recognizing the signs early saves painful rewrites.

Sign

The chain keeps growing

More than five steps, or steps that only make sense together, suggest real logic is hiding in settings.

Sign

People ask for lookups

“Can we add the customer’s region?” needs data from another source, which an SMT cannot fetch safely.

Sign

People ask for counts

Any request that includes the words “so far”, “per hour” or “in total” needs state.

Sign

Other teams want the result

If several consumers need the cleaned data, it should live in a topic of its own.

Everyday Analogy

A small home kitchen is perfect for a family. When the family starts a catering business, adding more and more gadgets to the same counter stops working. At some point you need a proper commercial kitchen. Moving your work to a stream processor is that step up.

How to migrate calmly

Moving logic out of an SMT chain does not have to be dramatic. Keep the light steps (unwrap, mask, rename) in the connector. Move the heavy steps into a Kafka Streams or ksqlDB job that reads the connector’s output topic and writes a new topic. Point the sink at that new topic. Run old and new side by side, compare counts, and switch over when they agree.

15Performance and Scalability

A well-chosen SMT is close to free. A badly chosen one can quietly become the slowest part of your pipeline.

Why they are usually cheap

An SMT runs inside the same thread as the task that owns the record, so there is no network hop and no extra queue. Most built-in transforms do a few small operations on data already in memory. The extra time per record is typically tiny compared with the time spent reading from the source, writing to the target or talking to Kafka.

How they scale

Because SMTs keep no state, they scale exactly as the connector scales. If you raise tasks.max and the source or sink can be divided further, each new task brings its own copy of the transform chain. There is nothing to move, restore or synchronize between tasks. This is the same reason stateless processing is easy to spread across many machines.

In-thread
runs inside the task, no extra hop
Stateless
no storage or recovery to plan for
Per record
cost grows with every step in the chain

What can make them slow

Remote calls

Network inside a transform

A call to an outside service for each record turns microseconds into milliseconds and can stall the task when that service is slow.

Schema rebuilding

New schema per record

Rebuilding a schema object for every record wastes effort. Good transforms build it once and reuse it.

Heavy patterns

Costly pattern matching

Very complex regular expressions on large text fields can consume real processor time at high volume.

Long chains

Too many steps

Each step copies or rebuilds parts of the record. Ten steps do roughly ten times the work of one.

Everyday Analogy

A tollbooth that takes two seconds per car keeps a highway flowing. Ask each driver a fifteen-second survey question and the queue stretches for miles. Adding more booths helps, but making each booth faster helps more. SMTs are best kept at “two seconds” or less.

Practical rules

  • Put filters near the start of the chain so later steps handle fewer records.
  • Prefer value-only or key-only versions rather than transforming both when only one matters.
  • Measure the connector’s throughput with and without the chain on a test topic.
  • If a transform needs outside data, bring that data into Kafka and use a stream processor instead.

A back-of-the-envelope example

Numbers help build feel. The figures below are illustrative, not measurements from a real system, so measure your own chain before relying on them.

10,000
records per second handled by one task
5
transforms in the chain
10 µs
assumed cost per transform per record

Five steps at ten microseconds each cost fifty microseconds per record. At ten thousand records per second, that is about half a second of processor time for every second of clock time, so roughly half of one processor core. That is real, though still modest. Now imagine one custom step that calls a web service taking five milliseconds. That single step costs five thousand microseconds per record, one hundred times the whole rest of the chain, and at ten thousand records per second it would need fifty seconds of work every second. The task could never keep up, and adding more tasks would only hammer the service harder.

Everyday Analogy

Think of a school corridor at the end of the day. If each child pauses for a second to tie a shoelace, the corridor flows. If each child stops to have a long conversation with a teacher, the corridor jams no matter how wide it is. The chain is the corridor, and a slow step is the long conversation.

i
Tip

Whenever you add a step, do this quick sum: cost per record multiplied by records per second. If the answer is a large fraction of a processor core, reconsider the design before it goes live.

16Monitoring, Logging and Security

SMTs are small, but they sit on the path of every record. That makes them both a useful place to protect data and a place where mistakes can hide.

What to watch

SignalWhat it tells you
Connector and task statusWhether tasks are running, failed or paused
Record read and write ratesWhether the connector still moves the volume you expect
Error, skipped and retry countsWhether transforms or converters are rejecting records
Dead letter topic sizeHow many records failed and are waiting for a person
Lag on the sink’s input topicsWhether a slow chain is holding the sink back
Output record counts versus input countsWhether a filter is dropping more than it should
Everyday Analogy

Watching an SMT chain is like counting parcels at both ends of a sorting machine. If a thousand go in and nine hundred come out, you want to know whether a hundred were meant to be thrown away. A silent gap between input and output is the classic sign of a filter that is too eager.

Logging with care

Good logs name the connector, the task and the transform alias that failed. Avoid printing whole records into logs unless you must, because records may carry the very personal data you set out to mask. If you do turn on message logging to debug a problem, turn it off again afterwards and treat the log files as sensitive.

Security points

Masking

Hiding is not encrypting

A masked field is gone from that copy for good. If the original must be recoverable later, use proper encryption at the application level instead.

Where you mask

Mask before it spreads

Masking on a sink protects the target, but the topic still holds raw data. Masking on a source protects everything downstream.

REST access

Guard the control panel

Anyone who can change a connector’s settings can change its transforms. Restrict and audit access to the Connect REST interface.

Plugins

Trust what you install

A custom transform runs with the worker’s permissions. Review the code, build it in a controlled pipeline and store the JAR safely.

Dead letters

Failed records hold data too

A dead letter topic contains the original, unmasked records. Give it the same access rules as the source topic.

Secrets

Keep passwords out of settings

Use Connect’s configuration providers so credentials are read from a safe store rather than pasted into connector settings.

!
Watch Out

Masking in a sink but leaving the source topic open to everyone is a common half-fix. Ask where the raw data lives and who can read it, not only what the target receives.

Change management and audits

Because transforms can hide or alter data, they deserve the same discipline as application code. Treat every change as a small release. Write down why it was made, who approved it and how it was tested. Keep the settings in a repository so an audit can show exactly which transforms were active on any given date.

For regulated data, add two extra habits. First, keep a written map of which connectors touch personal data, and which of them mask it. Second, test the masking regularly by reading a sample of output records and confirming that private fields are truly gone. A rule that was correct last year may have been broken by a new field added at the source.

Everyday Analogy

A pharmacy logs every change to a prescription: who changed it, when and why. Nobody enjoys the paperwork, yet it prevents serious harm. A transform chain that alters personal data deserves a similar trail.

17Design Patterns and Anti-Patterns

A few repeatable designs make SMT-based pipelines calm and predictable. A few shortcuts make them painful.

Patterns that work

Pattern

Unwrap, filter, mask, route

The standard four-step chain. It puts each concern in its own step, in an order that later steps can rely on.

Pattern

Clean at the boundary

Do format fixes at the edge, in the connector, so everything inside Kafka already uses agreed names and types.

Pattern

Guard with predicates

Apply risky transforms only to the topics or records they are meant for.

Pattern

Dead letter with alert

Tolerate bad records, save them, alert on the count and give someone ownership of the tray.

Pattern

Settings in version control

Keep connector settings in a repository and deploy them through a pipeline, so every change has an author and a history.

Pattern

Small custom plugins

One custom class per clear job, tested and versioned, rather than one giant transform that does everything.

Anti-patterns to avoid

ANTI-PATTERN 02 Avoid
What people do

They write a custom SMT that calls a database or web service for every record to fetch extra details.

Why it hurts

The connector now runs at the speed of that service. Outages of the service stall the pipeline, and a restart may repeat the calls. Load on the service rises with your traffic.

What to do instead

Publish the extra data to Kafka as a topic and join it in Kafka Streams or ksqlDB.

ANTI-PATTERN 03 Avoid
What people do

They build business rules into long transform chains, with many conditions, renames and special cases.

Why it hurts

Settings are hard to read, test and review. Nobody can tell what a record looks like after step nine, and a small edit at step three breaks steps seven and eight.

What to do instead

Keep SMTs for plumbing. Move real business logic into a stream processor where it can be written as code, tested and versioned.

ANTI-PATTERN 04 Avoid
What people do

They test transforms only with clean, typical records.

Why it hurts

Production always contains the odd record: a missing field, a null, a deletion marker, an unexpected type. The first such record halts the task or is silently mangled.

What to do instead

Build a small test set of awkward records and run every chain change against it before release.

ANTI-PATTERN 05 Avoid
What people do

They expect an SMT to fix data in the topic for every consumer.

Why it hurts

A sink-side transform changes only what that sink writes. Other consumers of the topic still see the raw records, and the team wrongly believes the data is clean everywhere.

What to do instead

Clean data at the source connector, or produce a cleaned topic with a stream processor for all consumers.

18Best Practices and Common Mistakes

These lessons show up again and again in teams that run Kafka Connect for years.

Do

  • Use built-in transforms first and custom ones only when needed.
  • Name aliases clearly so logs and reviews make sense.
  • Order the chain deliberately: unwrap, filter, mask, route.
  • Use predicates to limit transforms to the records they belong to.
  • Test with awkward records: nulls, missing fields, tombstones.
  • Set an error policy, a dead letter topic and an alert on its size.
  • Compare input and output counts after every change.
  • Keep connector settings in version control.

Do not

  • Use SMTs to count, join, sort, split or deduplicate.
  • Call outside services from a transform.
  • Build long chains full of business rules.
  • Add Filter without a predicate.
  • Change keys without thinking about partitioning.
  • Assume a sink-side change cleans the topic for everyone.
  • Leave message logging switched on in production.
  • Forget to update settings when the source format changes.

The mistakes beginners meet first

1

Nothing seems to change

Usually the key flavour was chosen where the value flavour was needed, or the other way round. Check which part of the record you meant to touch.

2

The connector runs but the target is empty

A Filter with no predicate, or a predicate that matches everything, drops all records. Check the alias list and the predicate settings.

3

A task fails on one record

A field is missing, null or the wrong type, or the record is a tombstone. Add a predicate or fix the source.

4

A transform works on the source but not on the sink

The data shape is different on each side because of the converter position. Test each side with its own sample records.

5

The plugin cannot be found

The JAR is missing on one worker, the plugin path is wrong, or workers were not restarted. Check every worker, not just one.

6

Ordering breaks after a key change

New records use the new key and land in different partitions from old ones. Plan key changes as migrations.

Everyday Analogy

Running SMTs well is like keeping a garden path tidy. A little sweeping each week keeps it clear, but if you never look, one fallen branch can block the route for days before anyone notices. Regular counts, alerts and tests are the weekly sweep.

i
Tip

Write a one-line comment beside each transform in your repository explaining why it exists. Months later, a stranger will thank you for it, and that stranger may be you.

A short launch checklist

Before any new transform chain reaches production, walk through these steps in order.

1

Validate the settings

Use the validation call to catch spelling errors and missing options.

2

Try a sample topic

Send hand-made records, including odd ones, through the chain and inspect the output.

3

Check counts

Confirm the number of records leaving matches what you expect after filtering.

4

Confirm the error policy

Decide what happens to bad records, and make sure the dead letter topic and alerts are in place.

5

Roll out and watch

Start with one connector or one environment, watch error and throughput charts, and only then extend.

i
Note

A rollback plan is part of the launch. Keep the previous settings ready, because reverting a chain is usually as simple as updating the connector back to the older version.

19Real-World Stories in One Place

The projects and products below are publicly documented, and they show the same few SMT ideas at work in different settings.

Debezium and change-data-capture

Debezium turns database changes into Kafka events. Its unwrapping transform is one of the most widely used non-built-in SMTs, because nearly every team that consumes change events wants the simple row, not the full envelope. This is a textbook single-record edit.

Debezium and the outbox pattern

The outbox event router uses a column in each row to pick the destination topic and to shape the message. It replaces what would otherwise be a small custom service with a few lines of settings.

Elastic and time-based indexes

Search teams commonly want one index per day so that old data can be dropped cleanly. A routing transform on the sink connector gives every record a date-based destination without any code.

Amazon S3 and time-based folders

Object storage sinks use topic names to build folder paths. Renaming topics with a pattern or a date at the sink lets teams organize storage neatly, for example one folder per topic per day.

Confluent and managed connectors

Because Confluent’s cloud service runs Connect for its customers, many of them use SMTs without ever touching a worker. The same ideas of masking, routing and renaming carry over unchanged.

LinkedIn and the origin of Kafka

Kafka began at LinkedIn to move huge streams of activity events. As Kafka spread, teams needed a standard way to connect it to other systems, and that need produced Kafka Connect and later SMTs. The lesson still holds: move data with a framework, and do only small edits along the way.

In every one of these stories, the transform is small, the effect is large, and no extra service is needed.

20Frequently Asked Questions

Short answers to the questions beginners ask most often.

Q1What does SMT stand for?
It stands for Single Message Transform. It is a small edit applied to one record at a time inside Kafka Connect, either just before the record is written to Kafka or just after it is read from Kafka.
Q2Can I use SMTs with a plain producer or consumer?
No. SMTs belong to Kafka Connect. For plain producers and consumers you write the edit in your own code, and for richer processing you use Kafka Streams or ksqlDB.
Q3Can an SMT change several records into one, or one into several?
No. An SMT receives one record and returns one record or nothing. Merging, counting and splitting need a stream processor.
Q4Are SMTs stateful?
No. They are stateless by design and do not remember earlier records. That is why they are cheap and easy to scale.
Q5Does an SMT change the data stored in the Kafka topic?
Not on a sink connector. A sink-side transform only changes what that sink receives. A source-side transform changes what is written into the topic, so the stored data does carry the change from then on.
Q6In what order do transforms run?
In exactly the order listed in the connector’s transforms setting. The output of each becomes the input of the next, and a transform that returns nothing ends the chain for that record.
Q7What is a predicate?
A predicate is a yes-or-no test on a record, such as whether its topic matches a pattern or it carries a certain header. It lets a transform run only for the records you choose. Predicates arrived in Kafka Connect in version 2.6.
Q8What happens if a transform throws an error?
By default the task stops. You can choose to skip failing records, log them, retry, and, for sink connectors, save them to a dead letter topic with details about the failure.
Q9Is masking with an SMT the same as encryption?
No. Masking replaces a value with a placeholder and the original cannot be recovered from that copy. Encryption keeps the value recoverable by anyone holding the key.
Q10Do SMTs slow my connector down?
Built-in transforms add very little time per record. Slowness usually comes from very long chains, heavy pattern matching or custom transforms that call outside services.
Q11Can I write my own SMT?
Yes. You write a class that follows the Transformation contract, package it as a JAR, place it on every worker’s plugin path, restart the workers and reference the class name in your connector settings.
Q12Should I use an SMT or Kafka Streams for a simple filter?
If the data already flows through Kafka Connect and the filter is a simple per-record rule, an SMT is easiest. If the data does not use Connect, or other consumers need the filtered result too, use a small stateless step in Kafka Streams.
Q13Can two connectors on the same topic use different transforms?
Yes. Transforms belong to each connector, so one sink can mask fields and rename topics while another sink reads the same topic untouched. The stored data in the topic is not changed by a sink-side transform.
Q14Why did my transform work in testing but fail in production?
Production data contains cases that test data lacked: missing fields, nulls, tombstones, unexpected types and schema changes. Build a small test set of awkward records and run every chain change against it.
Q15How do I know whether a filter is dropping too much?
Compare the number of records read with the number written, and watch the error and skipped-record counts. A sudden drop in output with a healthy connector often means a filter or predicate is matching more than intended.
Q16Do SMTs work with every connector?
The transform mechanism is part of Kafka Connect itself, so it works with any connector that runs in Connect. What a transform can do still depends on the shape of the data that connector produces or consumes. Managed services may allow only a subset of transforms.
Q17Should I mask data at the source or at the sink?
Mask at the source when nobody downstream should see the raw values, because the topic then never holds them. Mask at the sink when some teams may read the raw topic but a particular target must not receive it. Many organizations do both for sensitive fields.
Q18Are SMTs the same as Single Message Transforms in other messaging tools?
The term belongs to Kafka Connect. Other tools have similar ideas under different names, such as processors or mapping steps, but the settings, the built-in list and the rules described in this guide are specific to Kafka Connect.

21Summary and Key Takeaways

If you keep only one page of this guide, keep this one.

Single Message Transforms are small, stateless edits that Kafka Connect applies to each record as it enters or leaves Kafka. They are perfect for plumbing work such as renaming, masking, routing and filtering, and they are the wrong tool for anything that needs memory or more than one record at a time.

Key Takeaways

  • One record in, zero or one out. An SMT edits or drops a single record and never remembers earlier ones.
  • They live in Kafka Connect. On sources they run before the converter, and on sinks they run after it.
  • Built-ins cover most needs. Field shaping, masking, key setting, topic routing, time conversion and header handling need only settings, not code.
  • Order and shape matter. Chains run top to bottom, and schema or schemaless data changes what a transform can do.
  • Use predicates and tests. Apply transforms only where intended, and always test with awkward records such as nulls and tombstones.
  • Plan for failure. Choose an error policy, use a dead letter topic on sinks, and alert when records are skipped or dropped.
  • Know the limits. Counting, joining, splitting and outside lookups belong in Kafka Streams, ksqlDB or your own services, not in an SMT.