<a id="cp-associate-existing-schema"></a>

# 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](#cp-explicit-associations-clients). If the topic has no<br/>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<br/>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<br/>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<br/>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](#cp-explicit-associations-clients). For details on the other legacy
strategies, see [Subject name strategy](serdes-develop/index.md#sr-schemas-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](#cp-explicit-associations-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.

<a id="cp-explicit-associations-concepts"></a>

## Key concepts

| Term                     | Meaning                                                                                                                                                                                                                                    |
|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Subject                  | The named container in Schema Registry where a schema and its versions are<br/>registered. For example, `orders-value`.                                                                                                                    |
| Association              | The link between a topic and a subject. It has a lifecycle of<br/>`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<br/>`WEAK`.                                                                                                                                                               |
| `AssociatedNameStrategy` | The subject name strategy that clients use by default on supported<br/>client library versions. It uses a topic’s association to find the<br/>subject, and falls back to `TopicNameStrategy` if the topic<br/>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<br/>cluster ID.                                                                               | Any existing subject, whatever its name.                                  |
| Reserves the subject      | Yes. Schema Registry rejects the association if the subject already has any other<br/>association, and rejects any other association on that subject<br/>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](#cp-explicit-associations-clients).

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:

```json
{
  "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:

```java
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:

```json
{
  "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:

```http
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:

```java
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:

```java
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:

```http
GET /associations/subjects/Customer
```

Example response:

```json
[
  {
    "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:

```http
GET /associations/resources/{cluster_id}/orders
```

`{cluster_id}` is the Kafka cluster ID.

Example response:

```json
[
  {
    "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<br/>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.

<a id="cp-explicit-associations-clients"></a>

### 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.<br/>* 8.1.x: 8.1.2 or later.<br/>* 8.2.x: 8.2.2 or later.<br/>* 8.3.0 and later.<br/><br/>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                                                                                                                                            |

### 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](../schema-validation.md#sr-associated-name-strategy).

### 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.
