Explicit Topic-Subject Associations

An explicit topic-subject association links a Kafka topic directly to a Schema Registry subject.

Clients use a subject name strategy to decide which subject to register a topic’s schema under and read it from:

Strategy

Association type

How it finds the subject

Subject name

When it applies

AssociatedNameStrategy

Explicit

Looks up the subject associated with the topic

The subject the association points to

The default on client library versions that support it. If the topic has no association, it falls back to TopicNameStrategy.

TopicNameStrategy

Legacy implicit

Derives the name from the topic

<topic_name>-key or <topic_name>-value

The default on earlier client library versions, and the default fallback. All messages in a topic use the same schema.

RecordNameStrategy

Legacy implicit

Derives the name from the record

The fully qualified record name

A topic carries more than one record type, and you want compatibility checked per record type across all topics.

TopicRecordNameStrategy

Legacy implicit

Derives the name from the topic and the record

<topic_name>-<fully_qualified_record_name>

A topic carries more than one record type, and you want compatibility checked per record type within each topic.

The strategies other than AssociatedNameStrategy derive the subject name from the topic or the record each time a client runs. Nothing records which subject a topic uses, and nothing stops someone from deleting that subject while the topic still uses it. An association records the relationship and prevents deletion of the subject while a topic uses it.

For the client library versions that support AssociatedNameStrategy, see Supported client library versions. For details on the other legacy strategies, see Subject name strategy.

Explicit associations introduce these distinct architectural patterns:

  • Topic-native schemas, also called native schemas, where a schema is strictly bound to a single topic. The schema exists only while the topic exists, like a table schema in a database.

  • Shared schemas, where a single schema can be explicitly linked across more than one topic. Schema Registry subjects already work this way. The subject stays when you remove the association.

A topic created with a schema gets a native schema, and a topic associated with an existing subject gets a shared schema. The API reports each association’s lifecycle, STRONG for native and WEAK for shared. See Key concepts.

Create, change, and delete associations by setting the topic configurations confluent.key.association and confluent.value.association. Use the Kafka Admin API, for example the createTopics and incrementalAlterConfigs requests. Don’t call the Schema Registry Associations API to write associations. Schema Registry sets each association’s lifecycle value based on how you create the topic. You don’t specify it. You can call the Schema Registry Associations API to read associations.

Key concepts

Term

Meaning

Subject

The named container in Schema Registry where a schema and its versions are registered. For example, orders-value.

Association

The link between a topic and a subject. It has a lifecycle of STRONG or WEAK.

Native schema

A schema owned by one topic. It has a lifecycle of STRONG.

Shared schema

A schema that more than one topic can use. It has a lifecycle of WEAK.

AssociatedNameStrategy

The subject name strategy that clients use by default on supported client library versions. It uses a topic’s association to find the subject, and falls back to TopicNameStrategy if the topic doesn’t have an association.

How native and shared schemas differ

Native schema (STRONG)

Shared schema (WEAK)

How you create it

Together with the topic, through the Kafka API.

Register the subject in Schema Registry, then associate a topic with it.

Topics that can use it

One

Many

Subject name

By default, <topic_name>-value, in a schema context named after the cluster ID.

Any existing subject, whatever its name.

Reserves the subject

Yes. Schema Registry rejects the association if the subject already has any other association, and rejects any other association on that subject afterward.

No

Subject deletion

Blocked while the topic exists.

Blocked while any topic has an association.

When you delete the topic

Schema Registry deletes the association, the subject, and every schema version.

Schema Registry removes the association. The subject and its schema stay.

Removing the association

Only by deleting the topic.

Unlink the topic at any time.

You can associate an existing subject only with a topic that doesn’t already have a native schema.

Prerequisites

  • Brokers with confluent.topic.subject.association.enable set to true. Brokers accept association settings only with that setting.

  • Client libraries that support AssociatedNameStrategy. See Supported client library versions.

The following examples use three resources:

  • orders: a topic with its own native Order schema.

  • Customer: a shared schema registered on its own.

  • customer-signups: a topic associated with the shared Customer schema.

Create a topic with a native schema

Create the topic and its schema in one request through the Kafka API. The schema belongs to the topic. It evolves with the topic, and Schema Registry deletes it when you delete the topic.

To create the schema with the topic, send a createTopics request that sets confluent.value.association. For a key schema, set confluent.key.association. The value is a JSON object with schemaType and schema.

By default, Schema Registry names the subject <topic_name>-value and places it in the schema context :.<cluster_id>:, where <cluster_id> is the 22-character Kafka cluster ID, like MkU3OEVBNTcwNTJENDM2Qk. To choose a different name, set subject in the JSON object.

Note

Deleting a topic that has a native association also deletes its subject and every schema version.

These examples use the Order schema:

{
  "type": "record",
  "name": "Order",
  "namespace": "com.example.avro",
  "fields": [
    { "name": "orderId", "type": "string" },
    { "name": "amount", "type": "double" }
  ]
}

The following example creates the orders topic and registers its Order schema as a native schema:

Properties props = new Properties();
props.put("bootstrap.servers", "<kafka-endpoint>");

try (AdminClient adminClient = AdminClient.create(props)) {
    NewTopic topic = new NewTopic("orders", 1, (short) 1);

    Map<String, String> valueAssociation = new HashMap<>();
    valueAssociation.put("schemaType", "AVRO");
    valueAssociation.put("schema",
        "{\"type\":\"record\",\"name\":\"Order\","
        + "\"namespace\":\"com.example.avro\","
        + "\"fields\":[{\"name\":\"orderId\",\"type\":\"string\"},"
        + "{\"name\":\"amount\",\"type\":\"double\"}]}");

    Map<String, String> topicConfig = new HashMap<>();
    topicConfig.put("confluent.value.association",
        new ObjectMapper().writeValueAsString(valueAssociation));
    topic.configs(topicConfig);

    adminClient.createTopics(Collections.singletonList(topic)).all().get();
}

Use a shared schema across topics

A shared schema is a subject that you register one time and associate with any topic that doesn’t already have a native schema. Other topics can keep using it, and Schema Registry blocks deletion of the subject while any topic has an association with it.

To share a schema, register the shared schema, and then associate a topic with it.

Register the shared schema

Registering a shared schema is ordinary Schema Registry registration. These examples use the Customer schema:

{
  "type": "record",
  "name": "Customer",
  "namespace": "com.example.avro",
  "fields": [
    { "name": "customerId", "type": "string" },
    { "name": "email", "type": "string" }
  ]
}

Register it with the Schema Registry REST API:

POST /subjects/Customer/versions
Content-Type: application/vnd.schemaregistry.v1+json

{
  "schemaType": "AVRO",
  "schema": "{\"type\":\"record\",\"name\":\"Customer\",\"namespace\":\"com.example.avro\",\"fields\":[{\"name\":\"customerId\",\"type\":\"string\"},{\"name\":\"email\",\"type\":\"string\"}]}"
}

You can also register shared schemas from a CI/CD pipeline with the Schema Registry Maven or Gradle plugin.

Associate a topic with the shared schema

If you give the topic a subject and no schema definition, Schema Registry associates an existing subject and doesn’t create a native schema. The lifecycle is always WEAK.

To associate a topic with an existing subject, send an incrementalAlterConfigs request that sets confluent.value.association on the topic. For a key schema, set confluent.key.association. The following example associates the customer-signups topic with the Customer subject:

Map<String, String> valueAssociation = new HashMap<>();
valueAssociation.put("subject", "Customer");

ConfigResource topic =
    new ConfigResource(ConfigResource.Type.TOPIC, "customer-signups");
AlterConfigOp op = new AlterConfigOp(
    new ConfigEntry("confluent.value.association",
        new ObjectMapper().writeValueAsString(valueAssociation)),
    AlterConfigOp.OpType.SET);

adminClient.incrementalAlterConfigs(Map.of(topic, List.of(op))).all().get();

Remove an association

To remove a shared association, unlink the topic. The shared subject and its schema stay in Schema Registry, and other topics keep using them. After you remove the association, the topic’s confluent.value.association configuration returns to its default empty value. Schema Registry no longer lists the association for the topic.

You can’t remove a native association on its own. Schema Registry removes it when you delete the topic, which also deletes the subject.

To remove a key association, use confluent.key.association for the configuration name. To unlink the topic, send a DELETE operation on the configuration:

ConfigResource topic =
    new ConfigResource(ConfigResource.Type.TOPIC, "customer-signups");
AlterConfigOp op = new AlterConfigOp(
    new ConfigEntry("confluent.value.association", null),
    AlterConfigOp.OpType.DELETE);

adminClient.incrementalAlterConfigs(Map.of(topic, List.of(op))).all().get();

Query associations

Use the Schema Registry Associations API to find the subjects associated with a topic or the topics associated with a subject.

Look up by subject

Send this request to list the topics associated with the Customer subject:

GET /associations/subjects/Customer

Example response:

[
  {
    "subject": "Customer",
    "guid": "7a3c1f52-2d4e-4b8a-b6c1-9e0f5a1d2c33",
    "resourceName": "customer-signups",
    "resourceNamespace": "MkU3OEVBNTcwNTJENDM2Qk",
    "resourceId": "ZyXwVuTsRqPoNmLkJiHgFe",
    "resourceType": "topic",
    "associationType": "value",
    "lifecycle": "WEAK",
    "createTs": 1719000000000,
    "updateTs": 1719000000000
  }
]

Look up by topic

Send this request to list the subjects associated with the orders topic:

GET /associations/resources/{cluster_id}/orders

{cluster_id} is the Kafka cluster ID.

Example response:

[
  {
    "subject": ":.MkU3OEVBNTcwNTJENDM2Qk:orders-value",
    "guid": "5f4d6d3e-8b1a-4c2e-9a7f-1c9e8d2b3a4f",
    "resourceName": "orders",
    "resourceNamespace": "MkU3OEVBNTcwNTJENDM2Qk",
    "resourceId": "AbCdEfGhIjKlMnOpQrStUv",
    "resourceType": "topic",
    "associationType": "value",
    "lifecycle": "STRONG",
    "createTs": 1719000000000,
    "updateTs": 1719000000000
  }
]

createTs and updateTs are epoch milliseconds. guid is an internal identifier, not a schema ID.

Filters and paging

Both lookups accept these optional query parameters:

Parameter

Values

Example

resourceType

topic

GET /associations/subjects/Customer?resourceType=topic

associationType

key or value

GET /associations/subjects/Customer?associationType=key

lifecycle

STRONG or WEAK

GET /associations/subjects/Customer?lifecycle=WEAK

offset

Where to start. Defaults to 0.

GET /associations/subjects/Customer?offset=1

limit

The most results to return. Defaults to -1. Any negative value returns all results.

GET /associations/subjects/Customer?limit=1

An empty list means that no association matches your filters. A change to an association can take a few seconds to appear in lookup results.

Delete topics and subjects

Action

Native schema (STRONG)

Shared schema (WEAK)

Delete the topic

Deletes the association, the subject, and all schema versions.

Removes the association. The subject and its schema stay.

Delete the subject

Blocked while the topic exists.

Blocked while any topic has an association. Unlink the topics first.

Use associations with clients

On a supported client library version, AssociatedNameStrategy is the default subject name strategy, and you don’t need to configure anything. The strategy finds the topic’s associated subject first, and falls back to TopicNameStrategy when the topic has no association. Existing applications keep working unchanged. The same producer and consumer code works for native, shared, and unassociated topics.

Supported client library versions

AssociatedNameStrategy is the default from the versions in this table. Earlier versions default to TopicNameStrategy and don’t use associations to find the subject.

Language

Client library

Earliest supported version

Java Client

Confluent serializers (kafka-avro-serializer and others)

  • 8.0.x: 8.0.4 or later.

  • 8.1.x: 8.1.2 or later.

  • 8.2.x: 8.2.2 or later.

  • 8.3.0 and later.

Requires kafka-clients 4.1.0 or later.

Python

confluent-kafka-python

2.14.0

Go

confluent-kafka-go

2.14.0

Node.js

@confluentinc/kafka-javascript and @confluentinc/schemaregistry

1.9.0

.NET

confluent-kafka-dotnet and libschemaregistry

2.14.0 and 0.1.3

Caution

A producer on an earlier client library version doesn’t read associations. It builds the subject name from the topic name. For the topic customer-signups, it registers the schema under customer-signups-value and not the associated subject Customer. A producer that registers schemas automatically, the default, creates that second subject. You end up with two subjects for the same schema. To avoid this, upgrade the client library to a version in the preceding table.

Broker-side schema ID validation

Broker-side schema ID validation is an optional topic setting. When it’s on, the broker checks that the schema ID in each message is valid for the subject that the topic uses.

The broker uses AssociatedNameStrategy by default and finds the subject through the topic’s association. If the topic has no association, the broker falls back to TopicNameStrategy. You don’t need to configure anything on the topic. For details, see AssociatedNameStrategy.

Fallback strategy

If a topic has no association, AssociatedNameStrategy falls back to TopicNameStrategy by default. To choose the fallback, set subject.name.strategy.fallback.type on the client:

Value

Fallback

TOPIC

TopicNameStrategy (default)

RECORD

RecordNameStrategy

TOPIC_RECORD

TopicRecordNameStrategy

NONE

No fallback

Serialization fails with an error if the fallback is NONE and the topic has no association, or if the topic has more than one associated subject for the same key or value. Deserialization fails when the deserializer needs to resolve a subject. Examples include use.latest.version, use.latest.with.metadata, and migration rules. A deserializer that reads only the schema ID embedded in the message isn’t affected. Which deserializer settings resolve a subject varies by client library.

Replication across clusters

Cluster Linking doesn’t replicate associations. To replicate a topic that has an association, use Cluster Linking to mirror the topic and Schema Linking to replicate its schema. Then create the associations that exist on the source topic manually on the destination topic.