Examples
Practical recipes for common Apitomy Codegen use cases.
Reactive Endpoints
Wrap all generated return types in CompletionStage<> for non-blocking, asynchronous endpoints:
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<reactive>true</reactive>
</projectSettings>
Generated interface:
@Path("/beers")
public interface BeersResource {
@GET
@Produces("application/json")
CompletionStage<List<Beer>> listAllBeers();
@POST
@Consumes("application/json")
CompletionStage<Void> addBeer(@NotNull Beer data);
@DELETE
@Path("/{beerId}")
CompletionStage<Void> deleteBeer(@PathParam("beerId") int beerId);
}
Generic Return Type Wrapper
Wrap return types in a framework-specific response type like RESTEasy Reactive's RestResponse:
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<genericReturnType>org.jboss.resteasy.reactive.RestResponse</genericReturnType>
</projectSettings>
Generated interface:
@GET
@Produces("application/json")
RestResponse<List<Beer>> listAllBeers();
@DELETE
@Path("/{beerId}")
RestResponse<Void> deleteBeer(@PathParam("beerId") int beerId);
This lets your implementation control HTTP status codes and headers:
@Override
public RestResponse<Void> deleteBeer(int beerId) {
beerService.delete(beerId);
return RestResponse.noContent();
}
Combining Reactive + Generic Return Type
Both can be used together:
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<reactive>true</reactive>
<genericReturnType>org.jboss.resteasy.reactive.RestResponse</genericReturnType>
</projectSettings>
Produces CompletionStage<RestResponse<T>> return types.
Adding Lombok Annotations to All Beans
Use the x-codegen.bean-annotations extension in your OpenAPI spec to add Lombok (or any other) annotations globally:
{
"openapi": "3.0.3",
"info": { "title": "My API", "version": "1.0.0" },
"paths": { },
"components": {
"schemas": {
"User": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
}
}
}
},
"x-codegen": {
"bean-annotations": [
"lombok.ToString",
{
"annotation": "lombok.EqualsAndHashCode",
"excludeEnums": true
}
]
}
}
Generated bean:
@JsonInclude(JsonInclude.Include.NON_NULL)
@Generated("jsonschema2pojo")
@lombok.ToString
@lombok.EqualsAndHashCode
public class User {
// ...
}
The excludeEnums: true option on @EqualsAndHashCode means that annotation won't be added to generated enum classes.
Per-Schema Annotations
Add annotations to specific schemas using x-codegen-annotations:
{
"components": {
"schemas": {
"Event": {
"type": "object",
"x-codegen-annotations": [
"io.quarkus.runtime.annotations.RegisterForReflection"
],
"properties": {
"name": { "type": "string" }
}
}
}
}
}
Schema Inheritance
Use x-codegen-extendsClass to make a generated bean extend an existing class:
{
"components": {
"schemas": {
"RuleViolationError": {
"type": "object",
"x-codegen-extendsClass": "com.example.api.beans.Error",
"x-codegen-annotations": ["lombok.AllArgsConstructor", "lombok.Builder"],
"properties": {
"causes": {
"type": "array",
"items": { "$ref": "#/components/schemas/RuleViolationCause" }
}
}
}
}
}
}
Generated bean:
@lombok.AllArgsConstructor
@lombok.Builder
public class RuleViolationError extends Error {
// Only properties specific to this subclass
}
Context Root Prefix
Prepend a base path to all generated @Path annotations without modifying every path in your spec:
{
"paths": {
"/users": {
"get": { "operationId": "listUsers", "..." : "..." }
},
"/orders": {
"get": { "operationId": "listOrders", "..." : "..." }
},
"x-codegen-contextRoot": "/api/v2"
}
}
Generated interfaces:
@Path("/api/v2/users")
public interface UsersResource { }
@Path("/api/v2/orders")
public interface OrdersResource { }
Custom Return Type per Operation
Override the return type for a specific operation using x-codegen-returnType on the media type:
{
"paths": {
"/files/{fileId}": {
"get": {
"operationId": "downloadFile",
"responses": {
"200": {
"content": {
"application/octet-stream": {
"schema": { "type": "string", "format": "binary" },
"x-codegen-returnType": "jakarta.ws.rs.core.Response"
}
}
}
}
}
}
}
}
Generated method:
@GET
@Path("/{fileId}")
@Produces("application/octet-stream")
Response downloadFile(@PathParam("fileId") String fileId);
Selective Async Operations
Make specific operations async without enabling reactive globally:
paths:
/reports:
post:
operationId: generateReport
x-codegen-async: true
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ReportRequest"
responses:
"202":
description: Report generation started
/health:
get:
operationId: healthCheck
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/HealthStatus"
Generated interface:
// Async — returns CompletionStage
@POST
@Consumes("application/json")
CompletionStage<Void> generateReport(@NotNull ReportRequest data);
// Synchronous — returns directly
@GET
@Produces("application/json")
HealthStatus healthCheck();
Class Name Prefix/Suffix
Namespace generated classes to avoid collisions when generating from multiple specs:
<!-- First API -->
<execution>
<id>internal-api</id>
<configuration>
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<classNamePrefix>Internal</classNamePrefix>
</projectSettings>
<inputSpec>internal-api.json</inputSpec>
<outputDir>${project.build.directory}/generated-sources/internal</outputDir>
</configuration>
</execution>
<!-- Second API -->
<execution>
<id>public-api</id>
<configuration>
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<classNamePrefix>Public</classNamePrefix>
</projectSettings>
<inputSpec>public-api.json</inputSpec>
<outputDir>${project.build.directory}/generated-sources/public</outputDir>
</configuration>
</execution>
Produces InternalUsersResource, InternalUser vs. PublicUsersResource, PublicUser.
Bean Validation (JSR-303)
Enable validation annotations on generated beans based on OpenAPI schema constraints:
<projectSettings>
<javaPackage>com.example.api</javaPackage>
<useJsr303>true</useJsr303>
</projectSettings>
With this OpenAPI schema:
User:
type: object
required: [name, email]
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
pattern: "^[\\w.-]+@[\\w.-]+\\.\\w+$"
age:
type: integer
minimum: 0
maximum: 150
The generated bean includes validation annotations:
public class User {
@NotNull
@Size(min = 1, max = 100)
@JsonProperty("name")
private String name;
@NotNull
@Pattern(regexp = "^[\\w.-]+@[\\w.-]+\\.\\w+$")
@JsonProperty("email")
private String email;
@Min(0)
@Max(150)
@JsonProperty("age")
private Integer age;
}
Custom Date-Time Format
Override the default @JsonFormat pattern for a specific date-time property:
{
"components": {
"schemas": {
"Event": {
"type": "object",
"properties": {
"createdAt": {
"type": "string",
"format": "date-time"
},
"scheduledAt": {
"type": "string",
"format": "date-time",
"x-codegen-formatPattern": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
}
Generated bean:
// Default pattern
@JsonFormat(shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
@JsonProperty("createdAt")
private Date createdAt;
// Custom pattern
@JsonFormat(shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
@JsonProperty("scheduledAt")
private Date scheduledAt;
To suppress date-time formatting entirely, use the global extension:
Inline Simple Types
Prevent a simple schema from generating its own class by marking it as inline:
{
"components": {
"schemas": {
"UserId": {
"type": "string",
"x-codegen-inline": true
},
"User": {
"type": "object",
"properties": {
"id": { "$ref": "#/components/schemas/UserId" },
"name": { "type": "string" }
}
}
}
}
}
Instead of generating a UserId class, references to it are replaced with String directly.
Custom Bean Package
Place specific beans in a different package:
{
"components": {
"schemas": {
"AuditEvent": {
"type": "object",
"x-codegen-package": "com.example.api.audit",
"properties": {
"action": { "type": "string" },
"timestamp": { "type": "string", "format": "date-time" }
}
}
}
}
}
The AuditEvent class is generated in com.example.api.audit instead of the default com.example.api.beans.