apigenie-generatorAsyncAPI serversserver spring (AsyncAPI)
server spring (AsyncAPI)
Generate a Spring Boot application of the Kafka channels of an AsyncAPI document.
Usage
apigenie-generator server spring [...OPTIONS] INPUT_FILE
Description
apigenie-generator server spring generates from OpenAPI documents (see
apigenie-generator server spring) and from AsyncAPI 3.x
documents, telling them apart by their openapi or asyncapi field. Documents referencing other
files are bundled first.
From an AsyncAPI document it generates a Maven project of a Spring Boot 3 application using
Spring for Apache Kafka. By default the application
is the one the document describes: it sends the messages of send operations and receives the
messages of receive operations. Set the perspective option to counterpart to swap them.
The generator has one part per protocol. Today it generates the Kafka channels: the channels of
servers with the kafka or kafka-secure protocol, and channels with a Kafka binding in
documents without servers. Channels of other protocols are skipped and reported as info.
@Component
class LabelFarm implements RequestLabelRenderHandler {
@Override
public RequestLabelRenderReply onRenderLabel(RenderLabel message) {
LabelRenderResult result = render(message.payload());
return new LabelRendered(message.key(), result, new LabelRenderedHeaders(null));
}
}
@Service
class Shipments {
private final PublishShipmentEventsSender events;
void delivered(String shipmentId, ShipmentDeliveredData data, ShipmentDeliveredHeaders headers) {
events.sendShipmentDelivered(new ShipmentDelivered(shipmentId, data, headers));
}
}
The generated code has:
- models (package
model): a record per object schema the messages use, with Jackson and Jakarta Bean Validation annotations (@NotNullfor required properties,@Valid,@Pattern,@Size,@Min/@Max,@DecimalMin/@DecimalMax,@Email); an enum per enumeration; a sealed interface peroneOfof objects (@JsonTypeInfoby thediscriminatorproperty, or by deduction).allOfmembers are merged into one record. Schemas under#/components/schemaskeep their name; inline schemas are named after their message (PlaceOrderPayload) or their owner and property (LabelRenderRequestFormat). - messages (package
messages): a record per message with its Kafkakey, itspayloadand itsheaders, and a headers record with the message’s headers, the headers of its traits merged as AsyncAPI does (JSON Merge Patch: a laterrequiredlist replaces an earlier one). Headers with a constant value, e.g. thece_specversionandce_typeof CloudEvents in binary mode, are constants of the headers record (ShipmentStatusChangedHeaders.CE_TYPE), which senders write and listeners compare. The key is aStringvalidated by the key schema of the Kafka message binding. - Kafka (package
kafka):- for every received operation, a handler interface (
ConsumeOrdersHandler) with anon<Message>method per message, which you implement as a Spring bean, and a listener (ConsumeOrdersListener) with a@KafkaListenermethod. Several messages of a channel are told apart by a payload property or a header with a distinct constant value (e.g.typeorce_type); otherwise the first message whose payload is valid is chosen. Records matching no message go toonUnknown, which ignores them by default. - for every sent operation, a sender (
PublishShipmentEventsSender) with asend<Message>(message)method per message, over aKafkaTemplateusing the operation’s client id. - request/reply operations with a reply channel: the requester’s sender uses a
ReplyingKafkaTemplatelistening on the reply channel’s topic and returnsCompletableFuture<<Operation>Reply>, a sealed interface of the reply messages. Replies are matched by the header of the request message’scorrelationId, generated when the request has none. A replier’s handler returns the reply, which the listener sends to the reply topic with the request’s correlation id copied (from a header or a payload property, as thecorrelationIdlocations say). KafkaJson, converting payloads (JSON) and headers (UTF-8 text) and validating messages, andKafkaTopicsConfiguration, creating the topics of the channels from their Kafka bindings (partitions, replicas,topicConfiguration) whenasyncapi.kafka.create-topicsistrue.
- for every received operation, a handler interface (
- project files:
pom.xml(parentspring-boot-starter-parent,spring-boot-starter,spring-boot-starter-json,spring-boot-starter-validation(withbean_validation),spring-kafka, and the test starters), the@SpringBootApplicationclass andsrc/main/resources/application.yaml.
application.yaml points spring.kafka.bootstrap-servers to the first Kafka server (server
variables replaced by their default) and configures String keys and byte array values. The
asyncapi.kafka section holds every Kafka server, the topic of every channel and the group and
client ids of every operation, which the generated code reads, so that they can be changed without
regenerating:
asyncapi:
kafka:
create-topics: false
servers:
production-kafka:
bootstrap-servers: kafka-eu.events.carpetexpress.example:9093
schema-registry-url: https://schemas.events.carpetexpress.example
channels:
shipment-events:
topic: carpetexpress.shipment-events.v2
operations:
consume-carrier-scans:
group-id: tracking-ingest
client-id: carpet-express-consume-carrier-scans
Group and client ids come from the groupId and clientId schemas of the Kafka operation
bindings, of the operation or its traits: their const, single enum value, default or first
example. Without a value, <artifact id>-<operation> is used, with a comment naming the
binding’s pattern.
Constraints Bean Validation can’t express are line comments of the record or component: draft-07
dependencies, if/then/else, not, tuples (items arrays, mapped to List with @Size),
patternProperties, const values, and anyOf/oneOf branches constraining the properties of
an object.
Not supported, and reported as warnings: Avro payloads and payloads of other non-JSON schema
formats (the messages are skipped, and operations left without a message too), reply addresses
given by runtime expressions, and Kafka topics with parameters. Security (SASL/SCRAM, mTLS) and the
schema registry are not configured. With a correlation id in the payload, the replier’s reply must
carry the property (the listener overwrites it with the request’s), and a requester matches replies
by the kafka_correlationId header, which the replier must copy.
Extensions
| Extension | Effect |
|---|---|
x-apigenie.codegen.name |
name of the class of a schema |
x-apigenie.codegen.disabled |
true on a channel or an operation leaves it out (with the operations of a channel left out) |
The extensions can live in an overlay for this target instead of in the document, e.g.
spring.overlay.yaml:
overlay: 1.0.0
info:
title: Java names
version: 1.0.0
actions:
- target: $.components.schemas.Order
update:
x-apigenie:
codegen:
name: PurchaseOrder
apigenie-generator server spring asyncapi.yaml --overlay spring.overlay.yaml -o out
Options
-c, –config
Path to the configuration file to use. See the “Configuration” section for more information.
–overlay OVERLAY
Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those
listed under commands.generate.server_spring.overlay in the configuration file (with paths
relative to it). Warnings and errors about a value an overlay set point to the overlay. The overlays
apply to the AsyncAPI document bundled with the files it references.
–strict
Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).
-o, –output
Directory where the project is generated.
–package-name
Maven artifact id of the project. Defaults to the title of the API in kebab case. The base package
defaults to com.example. followed by the artifact id in lower case without other characters than
letters and digits (prefixed with app when it starts with a digit or is a Java keyword).
–package-version
Version of the project. Defaults to the version of the API.
-p, –property
Applies to OpenAPI documents only; using it with an AsyncAPI document is an error.
Configuration
The options are read from the asyncapi section of the configuration of apigenie-generator server spring;
its other keys configure the generation from OpenAPI documents.
version: '1.0'
commands:
generate:
server_spring:
asyncapi:
base_package: com.acme.events
group_id: com.acme
java_version: 21
spring_boot_version: 3.3.13
bean_validation: true
perspective: application
| Option | Description |
|---|---|
base_package |
Package of the generated code (default: com.example.<artifact id>); models are in .model, messages in .messages, the Kafka code in .kafka |
group_id |
Maven group id (default: the base package) |
java_version |
Java release, 17 (default) or later |
spring_boot_version |
Version of the Spring Boot 3 parent (default 3.3.13) |
bean_validation |
true (default): Bean Validation annotations, and validation of sent and received messages; false: none |
perspective |
application (default): the described application; counterpart: its counterpart, receiving what it sends |