> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streamnative.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Pulsar Schema Types

> Reference for the primitive, complex, and auto schema types supported by the Pulsar Schema Registry, and how they map to language-specific types.

A Pulsar schema is defined by a data structure called `SchemaInfo`. It's stored and enforced per
topic—you can't define one at the namespace or tenant level.

Here's a `SchemaInfo` for a string schema:

```json theme={null}
{
    "name": "test-string-schema",
    "type": "STRING",
    "schema": "",
    "properties": {}
}
```

| Field        | Description                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------- |
| `name`       | The schema name.                                                                                |
| `type`       | The [schema type](#schema-types), which determines how the data is serialized and deserialized. |
| `schema`     | The schema data: a sequence of 8-bit unsigned bytes whose meaning depends on the schema type.   |
| `properties` | A user-defined string-to-string map. Applications can use it to carry their own logic.          |

## Schema types

Pulsar schema types fall into three categories: [primitive](#primitive-types),
[complex](#complex-types), and [auto](#auto-schemas).

### Primitive types

| Primitive type               | Description                                                                                                                                            | Java                                                     | Python | Go      | C++         | C#                        |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | ------ | ------- | ----------- | ------------------------- |
| `BOOLEAN`                    | A binary value.                                                                                                                                        | boolean                                                  | bool   | bool    | bool        | bool                      |
| `INT8`                       | An 8-bit signed integer.                                                                                                                               | int                                                      | int    | int8    | int8\_t     | byte                      |
| `INT16`                      | A 16-bit signed integer.                                                                                                                               | int                                                      | int    | int16   | int16\_t    | short                     |
| `INT32`                      | A 32-bit signed integer.                                                                                                                               | int                                                      | int    | int32   | int32\_t    | int                       |
| `INT64`                      | A 64-bit signed integer.                                                                                                                               | int                                                      | int    | int64   | int64\_t    | long                      |
| `FLOAT`                      | A single-precision (32-bit) IEEE 754 floating-point number.                                                                                            | float                                                    | float  | float32 | float       | float                     |
| `DOUBLE`                     | A double-precision (64-bit) IEEE 754 floating-point number.                                                                                            | double                                                   | double | float64 | double      | double                    |
| `BYTES`                      | A sequence of 8-bit unsigned bytes.                                                                                                                    | byte\[], ByteBuffer, ByteBuf                             | bytes  | \[]byte | void \*     | byte\[], ReadOnlySequence |
| `STRING`                     | A Unicode character sequence.                                                                                                                          | string                                                   | str    | string  | std::string | string                    |
| `TIMESTAMP` (`DATE`, `TIME`) | A logical type representing an instant in time with millisecond precision, stored as milliseconds since `January 1, 1970, 00:00:00 GMT` in an `INT64`. | `java.sql.Timestamp` (`java.sql.Time`, `java.util.Date`) | N/A    | N/A     | N/A         | DateTime, TimeSpan        |
| `INSTANT`                    | A single instantaneous point on the timeline, with nanosecond precision.                                                                               | `java.time.Instant`                                      | N/A    | N/A     | N/A         | N/A                       |
| `LOCAL_DATE`                 | An immutable date-time object representing a date, usually viewed as year-month-day.                                                                   | `java.time.LocalDate`                                    | N/A    | N/A     | N/A         | N/A                       |
| `LOCAL_TIME`                 | An immutable date-time object representing a time to nanosecond precision, usually viewed as hour-minute-second.                                       | `java.time.LocalTime`                                    | N/A    | N/A     | N/A         | N/A                       |
| `LOCAL_DATE_TIME`            | An immutable date-time object representing a date and a time.                                                                                          | `java.time.LocalDateTime`                                | N/A    | N/A     | N/A         | N/A                       |

<Note title="Note">
  Pulsar stores no schema data in `SchemaInfo` for primitive types. Some primitive schema
  implementations use `properties` to hold implementation-specific settings—a string schema, for
  example, can store the character encoding it uses to serialize and deserialize strings.
</Note>

### Complex types

| Complex type | Description                                                                                          |
| ------------ | ---------------------------------------------------------------------------------------------------- |
| `KeyValue`   | A key/value pair.                                                                                    |
| `Struct`     | Structured data. Covers `AvroBaseStructSchema`, `ProtobufNativeSchema`, and `NativeAvroBytesSchema`. |

#### KeyValue schema

A `KeyValue` schema lets an application define a schema for the key and a schema for the value.
Pulsar stores both `SchemaInfo` records together.

There are two ways to encode a single key/value pair in a message:

* `INLINE`—the key and value are encoded together in the message payload.
* `SEPARATED`—the key is stored as the message key and the value as the message payload.

#### Struct schema

| Type                    | Description                                                                                                                                                                                                                                                                     |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AvroBaseStructSchema`  | Uses the [Avro specification](https://avro.apache.org/docs/current/spec.html) to declare the schema definition. Covers `AvroSchema`, `JsonSchema`, and `ProtobufSchema`. One set of tools manages the schema definitions while different serialization methods handle the data. |
| `ProtobufNativeSchema`  | Based on the Protobuf native descriptor. Uses native protobuf-v3 to serialize and deserialize data, and supports `AUTO_CONSUME`.                                                                                                                                                |
| `NativeAvroBytesSchema` | Wraps a native `org.apache.avro.Schema`. The resulting schema instance accepts a serialized Avro payload without validating it against the wrapped schema.                                                                                                                      |

`NativeAvroBytesSchema` exists for migration. When you ingest events from an external system such as
Kafka or Cassandra, the data is often already serialized as Avro and has already been validated
against a schema—including compatibility checks—by the system that produced it. In that case the
Pulsar producer doesn't need to repeat the validation. It passes each message through with its
schema.

<Tip>
  To use Kafka Avro, JSON Schema, or Protobuf schemas from a Pulsar client—with schemas resolved from
  the Kafka Schema Registry rather than Pulsar's—see
  [Use External Avro Schema with Pulsar clients](/cloud/governance/sr/external-schemas/external-avro-schema).
</Tip>

There are three ways to construct a struct schema.

<Tabs>
  <Tab title="Static">
    Predefine the struct as a POJO in Java, a struct in Go, or a class generated by Avro or Protobuf
    tooling. Pulsar reads the schema definition from it using an Avro library, and that definition
    becomes the schema data inside `SchemaInfo`.

    1. Define the class for the messages you send:

       ```java theme={null}
       // With Lombok
       @Builder
       @AllArgsConstructor
       @NoArgsConstructor
       public static class User {
           public String name;
           public int age;
       }

       // Without Lombok, add the constructors yourself:
       // public static class User {
       //     String name;
       //     int age;
       //     public User() { }
       //     public User(String name, int age) { this.name = name; this.age = age; }
       // }
       ```

    2. Create a producer with the struct schema and send a message:

       ```java theme={null}
       Producer<User> producer = client.newProducer(Schema.AVRO(User.class)).create();
       producer.newMessage().value(new User("pulsar-user", 1)).send();
       ```

    3. Create a consumer with the struct schema and receive it:

       ```java theme={null}
       Consumer<User> consumer = client.newConsumer(Schema.AVRO(User.class)).subscribe();
       User user = consumer.receive().getValue();
       ```
  </Tab>

  <Tab title="Generic">
    When your application has no predefined struct, define the schema with `GenericSchemaBuilder`, build
    records with `GenericRecordBuilder`, and consume into `GenericRecord`.

    1. Build a schema with `RecordSchemaBuilder`:

       ```java theme={null}
       RecordSchemaBuilder recordSchemaBuilder = SchemaBuilder.record("schemaName");
       recordSchemaBuilder.field("intField").type(SchemaType.INT32);
       SchemaInfo schemaInfo = recordSchemaBuilder.build(SchemaType.AVRO);

       Consumer<GenericRecord> consumer = client.newConsumer(Schema.generic(schemaInfo))
            .topic(topicName)
            .subscriptionName(subscriptionName)
            .subscribe();
       Producer<GenericRecord> producer = client.newProducer(Schema.generic(schemaInfo))
            .topic(topicName)
            .create();
       ```

    2. Build struct records with `RecordBuilder`:

       ```java theme={null}
       GenericSchemaImpl schema = GenericAvroSchema.of(schemaInfo);

       // Send a message
       GenericRecord record = schema.newRecordBuilder().set("intField", 32).build();
       producer.newMessage().value(record).send();

       // Receive a message
       Message<GenericRecord> msg = consumer.receive();
       Assert.assertEquals(msg.getValue().getField("intField"), 32);
       ```
  </Tab>

  <Tab title="SchemaDefinition">
    Define a `SchemaDefinition` and generate a struct schema from it.

    1. Define the class for the messages you send:

       ```java theme={null}
       public static class User {
           public String name;
           public int age;

           public User(String name, int age) {
               this.name = name;
               this.age = age;
           }

           public User() {}
       }
       ```

    2. Create a producer with a `SchemaDefinition` and send a message:

       ```java theme={null}
       SchemaDefinition<User> schemaDefinition = SchemaDefinition.<User>builder().withPojo(User.class).build();
       Producer<User> producer = client.newProducer(Schema.AVRO(schemaDefinition)).create();
       producer.newMessage().value(new User("pulsar-user", 1)).send();
       ```

    3. Create a consumer with the same `SchemaDefinition` and receive it:

       ```java theme={null}
       SchemaDefinition<User> schemaDefinition = SchemaDefinition.<User>builder().withPojo(User.class).build();
       Consumer<User> consumer = client.newConsumer(Schema.AVRO(schemaDefinition)).subscribe();
       User user = consumer.receive().getValue();
       ```
  </Tab>
</Tabs>

### Auto schemas

When you can't know a topic's schema type in advance, use an auto schema to produce or consume
generic records.

* `AUTO_PRODUCE` sends data to a topic that already has a schema, and validates that the outbound
  bytes are compatible with it.
* `AUTO_CONSUME` reads from a topic that has a schema and deserializes each message into a
  language-specific `GenericRecord`, using the `SchemaInfo` it retrieves from the broker.

## What's next

<CardGroup cols={2}>
  <Card title="Compatibility" icon="code-compare" href="/cloud/governance/sr/pulsar/compatibility">
    Compatibility strategies, versioning, and safe evolution.
  </Card>

  <Card title="Use with clients" icon="code" href="/cloud/governance/sr/pulsar/use-with-clients">
    Construct each schema type in your application.
  </Card>
</CardGroup>

<Note title="Attribution">
  Parts of this page are adapted from the [Apache Pulsar documentation](https://pulsar.apache.org/docs/schema-understand/),
  licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
</Note>
