openapi-get-avro is a deterministic Python CLI for producing Avro schemas from three kinds of input:
- OpenAPI JSON/YAML documents, using only selected
GETresponse schemas. - JSON files containing arrays of similar objects.
- JSON Schema documents, including local definitions and optional Confluent Schema Registry reference output.
The main OpenAPI workflow generates an Avro envelope record with fixed metadata fields and a data union containing one named record per selected GET response. The inference workflows generate regular Avro records from samples or JSON Schema.
Install from a built package or package index:
pip install openapi-get-avro
openapi-get-avro --helpWhen working from this repository, use the checked-in uv workflow instead:
uv sync
uv run openapi-get-avro --helpThe package requires Python 3.11 or newer.
| Command | Input | Output | Use when |
|---|---|---|---|
generate |
OpenAPI JSON/YAML | Avro envelope schema | You have API response schemas and want a Kafka value contract. |
infer-json |
JSON array of objects | Avro record schema | You have representative payload samples. |
infer-json-schema |
JSON Schema | Avro record schema | You already have a JSON Schema contract. |
All commands write the generated Avro JSON to --output when provided. If --output is omitted, generate and infer-json write to stdout. infer-json-schema also writes to stdout unless --references-output-dir is provided, in which case omitting --output means references-only mode.
Generate an Avro envelope from an OpenAPI document:
openapi-get-avro generate \
--input examples/minimal.openapi.json \
--namespace com.example.sports \
--rootname SportsEnvelope \
--output build/sports-envelope.avscInfer an Avro record from JSON samples:
openapi-get-avro infer-json examples/events.json \
--name Event \
--namespace com.example.events \
--output build/Event.avscInfer an Avro record from JSON Schema:
openapi-get-avro infer-json-schema examples/event.schema.json \
--name Event \
--namespace com.example.events \
--output build/Event.avscFrom a source checkout, prefix the same commands with uv run.
generate reads an OpenAPI JSON or YAML document and emits one Avro envelope schema. Only GET operations are considered. By default it includes status code 200 and response content type application/json.
openapi-get-avro generate \
--input examples/complex.openapi.yaml \
--namespace com.example.sports \
--rootname SportsEnvelope \
--output build/complex-sports-envelope.avscThe root record contains fixed metadata fields: id, timestamp, operation, entity_type, and data. The data field is an Avro union of named records generated from selected GET responses.
- Paths are processed deterministically.
- Non-
GETmethods are ignored. - Response status codes are selected by
--include-status-codes; default is200. - Response media type is selected by
--content-type; default isapplication/json. - Local component refs such as
#/components/schemas/Teamare resolved. - Remote refs and file refs are unsupported.
Select multiple response status codes in an explicit order:
openapi-get-avro generate \
--input examples/complex.openapi.yaml \
--namespace com.example.sports \
--rootname SportsEnvelope \
--include-status-codes 200,206,default \
--output build/sports-envelope.avscSelect a subset of generated response records:
openapi-get-avro generate \
--input examples/complex.openapi.yaml \
--namespace com.example.sports \
--rootname SportsEnvelope \
--include-response-records Venues,MatchParticipants \
--output build/selected-sports-envelope.avscSelectors include the full generated response record name and simplified forms without Response, status suffixes, leading Get, and leading Api. For example, Venues can match GetApiVenues200Response; VenuesAttributes must be specified separately for GetApiVenuesAttributes200Response.
Transform payload field names and remove generated type suffixes:
openapi-get-avro generate \
--input examples/complex.openapi.yaml \
--namespace com.example.sports \
--rootname SportsEnvelope \
--field-name-case snake_case \
--remove-name-suffixes Dto \
--output build/sports-envelope.avscUnwrap REST collection envelopes so the data union contains the item entity
record instead of a response wrapper with pagination fields:
openapi-get-avro generate \
--input examples/complex.openapi.yaml \
--namespace com.example.sports \
--rootname SportsEnvelope \
--response-shape unwrap-collection \
--output build/sports-envelope.avsc--response-shape preserve keeps the OpenAPI response shape. This is the
default. --response-shape unwrap-array unwraps top-level array responses when
the array items map to named records. --response-shape unwrap-collection also
unwraps common REST pagination envelopes with one payload array and metadata
fields such as pageNumber, totalPages, hasPreviousPage, and hasNextPage.
Required options:
| Option | Type | Description |
|---|---|---|
--input, -i |
path | OpenAPI JSON/YAML file. |
--namespace |
string | Avro namespace for generated named schemas. |
--rootname |
string | Root Avro envelope record name. |
General options:
| Option | Default | Description |
|---|---|---|
--output, -o |
stdout | Output .avsc path. |
--strict / --lenient |
--strict |
Fail on ambiguous constructs by default. |
Selection options:
| Option | Default | Description |
|---|---|---|
--include-status-codes |
200 |
Comma-separated response codes, evaluated in the order provided. |
--include-response-records |
include all | Comma-separated generated response record selectors. |
--content-type |
application/json |
Response content type to include. |
Naming and field options:
| Option | Default | Allowed values | Description |
|---|---|---|---|
--name-strategy |
operationId |
operationId, path |
Source for generated response record names. |
--field-name-case |
preserve |
preserve, snake_case, camelCase, PascalCase |
Transform payload field names only. |
--remove-name-suffixes |
none | comma-separated Avro name suffixes | Remove exact trailing suffixes from generated named types. |
Policy options:
| Option | Default | Allowed values | Description |
|---|---|---|---|
--any-of-policy |
fail |
fail, union |
Map anyOf to an Avro union only when requested. |
--enum-policy |
fail |
fail, string, sanitize |
Handle invalid enum values. |
--unknown-object-policy |
fail |
fail, map, string, empty-record |
Handle object schemas without clear properties. |
--response-shape |
preserve |
preserve, unwrap-array, unwrap-collection |
Normalize selected response root schemas before Avro conversion. |
--enforce-timestamp |
disabled | flag | Map date, date-time, and timestamp string formats to Avro timestamp-millis. |
Reference output options:
| Option | Default | Description |
|---|---|---|
--references-output-dir |
disabled | Also write Confluent Schema Registry referenced schemas to this directory. |
--references-manifest-output |
<references-output-dir>/manifest.json |
Manifest path for referenced schemas. |
--reference-subject-template |
{fullname} |
Subject template supporting {fullname}, {namespace}, {name}, and {rootname}. |
--root-subject |
template result | Schema Registry subject for the root envelope; use <topic>-value for TopicNameStrategy. |
With generate, providing --references-output-dir does not suppress the bundled schema. If --output is omitted, the bundled schema is still printed to stdout while reference files are written to disk.
infer-json reads a JSON file containing an array of similar objects and infers one Avro record schema.
openapi-get-avro infer-json examples/events.json \
--name Event \
--namespace com.example.events \
--output build/Event.avscFields present in every object are required. Fields missing from at least one object, or present with null at least once, become nullable fields with default: null. Nested objects and arrays are inferred recursively.
Ambiguous samples fail instead of guessing. Examples include mixed observed types such as 123 and "123", fields whose values are always null, and arrays whose observed items are always empty.
Infer timestamps and enums when samples are strong enough:
openapi-get-avro infer-json examples/events.json \
--name Event \
--namespace com.example.events \
--enforce-timestamp \
--enforce-enums \
--output build/Event.avscReuse structurally identical nested records:
openapi-get-avro infer-json examples/events.json \
--name Event \
--namespace com.example.events \
--reuse-record-shapes \
--output build/Event.avscInfer repeating object-valued properties as maps:
openapi-get-avro infer-json examples/tracking.json \
--name TrackingFrame \
--namespace com.example.tracking \
--enforce-map \
--output build/TrackingFrame.avscFor example, repeated { "x": 1.0, "y": 2.0, "z": 3.0 } object shapes can be reused as a shared Position record. Objects with primitive entries, such as { "home": 1, "away": 2 }, remain records so fixed field groups are not over-generalized.
Required argument and options:
| Argument or option | Type | Description |
|---|---|---|
input |
path | JSON file containing an array of objects. |
--name |
string | Avro record name. |
--namespace |
string | Avro namespace for inferred named schemas. |
Optional options:
| Option | Default | Description |
|---|---|---|
--output, -o |
stdout | Output .avsc path. |
--reuse-record-shapes |
disabled | Reuse structurally identical inferred records as Avro named references. |
--enforce-timestamp |
disabled | Infer ISO date and date-time strings as Avro timestamp-millis. |
--enforce-enums |
disabled | Infer string fields as Avro enums when all observed values are valid Avro enum symbols. |
--enforce-map |
disabled | Infer object-valued properties with repeating value shapes as Avro maps. |
infer-json-schema reads a JSON Schema document and emits one Avro record schema. It supports object schemas and root array schemas whose items describe an object.
openapi-get-avro infer-json-schema examples/event.schema.json \
--name Event \
--namespace com.example.events \
--output build/Event.avscLocal JSON Schema refs under #/definitions/... and #/$defs/... are supported. They are converted into Avro named records and enums. Remote refs and file refs are unsupported.
When the JSON Schema root is a pure $ref, the referenced definition becomes the Avro root record name. --root-subject applies to that generated root record when reference output is enabled.
Map anyOf to Avro unions when every branch maps to a unique named Avro type:
openapi-get-avro infer-json-schema examples/event.schema.json \
--name Event \
--namespace com.example.events \
--any-of-policy union \
--output build/Event.avscGenerate a bundled schema for code generation and reference files for Schema Registry:
openapi-get-avro infer-json-schema .local/aed/analytico.saed.review-inbox.schema.json \
--name AedEvent \
--namespace com.sts.analytico.saed \
--output build/com.sts.analytico.saed.review-inbox.avsc \
--references-output-dir build/review-inbox-references \
--root-subject analytico.saed.review-inbox-value \
--reference-subject-template "{fullname}"Generate only the referenced schemas and manifest:
openapi-get-avro infer-json-schema .local/aed/analytico.saed.review-inbox.schema.json \
--name AedEvent \
--namespace com.sts.analytico.saed \
--references-output-dir build/review-inbox-references \
--root-subject analytico.saed.review-inbox-value \
--reference-subject-template "{fullname}"References-only mode applies only to infer-json-schema: when --references-output-dir is provided and --output is omitted, the command writes reference files and the manifest, and does not print the bundled schema to stdout.
Required argument and options:
| Argument or option | Type | Description |
|---|---|---|
input |
path | JSON Schema file. |
--name |
string | Avro record name hint. |
--namespace |
string | Avro namespace for inferred named schemas. |
General and policy options:
| Option | Default | Allowed values | Description |
|---|---|---|---|
--output, -o |
stdout, or omitted in references-only mode | path | Output .avsc path. |
--any-of-policy |
fail |
fail, union |
Handle JSON Schema anyOf. |
--unknown-object-policy |
fail |
fail, map, string, empty-record |
Handle object schemas without clear properties. |
--enforce-timestamp |
disabled | flag | Map date, date-time, and timestamp string formats to Avro timestamp-millis. |
Reference output options:
| Option | Default | Description |
|---|---|---|
--references-output-dir |
disabled | Write Confluent Schema Registry referenced schemas to this directory. |
--references-manifest-output |
<references-output-dir>/manifest.json |
Manifest path for referenced schemas. |
--reference-subject-template |
{fullname} |
Subject template supporting {fullname}, {namespace}, {name}, and {rootname}. |
--root-subject |
template result | Schema Registry subject for the inferred root record. |
generate and infer-json-schema can emit Confluent-friendly referenced schemas in addition to, or instead of, a bundled schema depending on command and options.
The references directory contains one .avsc file per generated Avro named type plus a manifest. The manifest is deterministic and ordered for registration: dependencies first, then schemas that reference them, with the root schema last.
Manifest entries look like this:
{
"fullname": "com.example.Event",
"subject": "com.example.Event",
"file": "com.example.Event.avsc",
"references": [
{
"name": "com.example.Venue",
"subject": "com.example.Venue",
"version": "latest"
}
]
}Subject templates support:
| Placeholder | Meaning |
|---|---|
{fullname} |
Fully qualified Avro name, for example com.example.Event. |
{namespace} |
Avro namespace, for example com.example. |
{name} |
Short Avro name, for example Event. |
{rootname} |
Root record name for the current command. |
Use --root-subject <topic>-value when publishing the root schema with Confluent's default TopicNameStrategy.
Use the bundled schema from --output for tools that need a self-contained Avro schema, including many code generators such as avrogen. Schema Registry references are intended for registration workflows; a generated .avsc reference file may depend on other files listed in the manifest.
Typical dual-output workflow for JSON Schema:
openapi-get-avro infer-json-schema .local/aed/analytico.saed.review-inbox.schema.json \
--name AedEvent \
--namespace com.sts.analytico.saed \
--output build/com.sts.analytico.saed.review-inbox.avsc \
--references-output-dir build/review-inbox-references \
--root-subject analytico.saed.review-inbox-valueUse the bundled file for avrogen, and the manifest plus reference files for Schema Registry registration.
stringmaps to Avrostring.stringwithformat: uuidmaps to Avrostringwith logical typeuuid.stringwithformat: datemaps to Avrointwith logical typedateunless--enforce-timestampis set.stringwithformat: date-timemaps to Avrolongwith logical typetimestamp-millis.integermaps tolong;integerwithformat: int32maps toint.numbermaps todouble;numberwithformat: floatmaps tofloat.- Optional or nullable fields are emitted as Avro unions with
nullfirst anddefault: null. - Avro unions are JSON arrays. Avro does not use a literal
oneofkeyword. - Enums become Avro enums only when values are valid Avro enum symbols, unless a command option changes that behavior.
The generator is designed to behave like a compiler: same input and options should produce byte-for-byte stable output or fail with an actionable error.
Important deterministic behaviors include:
- OpenAPI paths are processed in sorted order.
- Response status code order follows
--include-status-codes. - OpenAPI and JSON Schema property order is preserved for fields.
- Generated names and reference manifests are stable.
- JSON output uses two-space indentation.
Most failures include the input path and, for OpenAPI conversion, the selected GET response context.
Common causes:
| Message or symptom | Command | Meaning and fix |
|---|---|---|
Unsupported $ref |
generate, infer-json-schema |
Only local component refs or local JSON Schema definitions are supported. Inline the schema or move it under supported local definitions. |
Invalid Avro enum symbol |
generate, infer-json-schema, infer-json --enforce-enums |
Avro enum symbols must match Avro name rules. Use valid symbols, choose an enum policy where available, or avoid enum inference. |
anyOf failure |
generate, infer-json-schema |
Default policy is fail. Use --any-of-policy union only when every branch maps to a unique named Avro type. |
| Free-form object failure | generate, infer-json-schema |
Object schemas without clear properties are ambiguous. Use --unknown-object-policy map, string, or empty-record when that mapping is acceptable. |
| Mixed sample types | infer-json |
Samples disagree on a field type, for example numbers and strings. Clean the samples or provide a JSON Schema instead. |
| Always-null field | infer-json |
The inferer cannot infer a type from only null. Add representative non-null values or use JSON Schema. |
| Always-empty array | infer-json |
The inferer cannot infer array item type. Include sample items or use JSON Schema. |
avrogen cannot resolve types from reference files |
reference output | Use the bundled schema produced by --output for code generation. Use reference files for Schema Registry registration. |
This repository uses uv for dependency management and command execution, Ruff for formatting and linting, mypy for type checking, and pytest for tests.
Set up the environment:
uv syncRun the standard checks:
uv run ruff format --check .
uv run ruff check .
uv run mypy src
uv run pytestThe same checks are available through make:
make checkUseful development commands:
uv run ruff format .
uv run ruff check --fix .
uv lock
uv treeAfter uv sync, select .venv/bin/python as the VS Code interpreter on Linux/macOS, or .venv\Scripts\python.exe on Windows. Ruff is configured as the formatter for Python files.