| layout | default |
|---|---|
| title | Client-Side Adoption |
| parent | Adoption Guides |
| nav_order | 2 |
| has_toc | false |
Generate Java clients that reconstruct your published contract instead of redefining it.
This guide explains how to generate contract-aligned Java clients from an OpenAPI Generics document.
For server-side projection, see Server-Side Adoption.
For architecture details, see Architecture.
- Quick Start
- Contract-Driven Envelope Reconstruction
- BYOE — Bring Your Own Envelope
- BYOC — Bring Your Own Contract
- Application-Defined Generic Containers
- Supported Contracts
- Fallback Mode
- Verification
- Usage Boundary
- Further Reading
<parent>
<groupId>io.github.blueprint-platform</groupId>
<artifactId>openapi-generics-java-codegen-parent</artifactId>
<version>1.2.1</version>
</parent>The parent provides the OpenAPI Generics-specific template preparation, generator integration, tested OpenAPI Generator alignment, source registration, and generated-source hygiene required for reconstruction.
Use the OpenAPI Generator Maven plugin as usual, but select the OpenAPI Generics Java generator:
<generatorName>java-generics-contract</generatorName>That is the OpenAPI Generics integration point.
Standard OpenAPI Generator options such as library, apiPackage, modelPackage, invokerPackage, and Spring/Jackson configuration remain normal generator choices.
Example:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-client</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generatorName>java-generics-contract</generatorName>
<inputSpec>
${project.basedir}/src/main/resources/api-docs.yaml
</inputSpec>
<!-- Standard OpenAPI Generator choice. -->
<library>restclient</library>
<!-- Standard generated package layout. -->
<apiPackage>com.example.client.api</apiPackage>
<modelPackage>com.example.client.dto</modelPackage>
<invokerPackage>com.example.client.invoker</invokerPackage>
<configOptions>
<useSpringBoot3>true</useSpringBoot3>
<serializationLibrary>jackson</serializationLibrary>
<openApiNullable>false</openApiNullable>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>Expected generated wrapper shape:
public class ServiceResponseCustomerDto
extends ServiceResponse<CustomerDto> {
}Container example:
public class ServiceResponsePageCustomerDto
extends ServiceResponse<Page<CustomerDto>> {
}Page<T> is imported from openapi-generics-contract.
The generated wrapper itself is emitted under the configured modelPackage.
mvn clean installGenerated sources are added automatically under:
target/generated-sources/openapi/src/gen/java
The generator reconstructs supported wrapper and container contracts from OpenAPI Generics metadata in the input document.
OpenAPI Generics reconstructs envelope identity from contract metadata carried by the OpenAPI document when producer and codegen components are aligned.
The producer publishes the envelope type through:
x-api-wrapper-type: io.example.contract.ApiResponseThe Java generator consumes that metadata during wrapper reconstruction.
Producer contract
↓
x-api-wrapper-type
↓
OpenAPI document
↓
java-generics-contract
↓
Generated wrapper
The generated OpenAPI document is therefore the source of reconstruction metadata for the client generator.
The same envelope does not need to be declared again on the client through openapi-generics.envelope.
This removes duplicated envelope configuration across producer and client generation.
The envelope type itself must still be available on the client classpath because generated wrappers extend the original contract type.
BYOE allows the generated client to reuse an application-owned response envelope instead of ServiceResponse<T>.
The envelope is configured on the producer side:
openapi-generics:
envelope:
type: com.example.contract.ApiResponseThe generated OpenAPI document preserves that identity through:
x-api-wrapper-type: com.example.contract.ApiResponseThe client generator then reconstructs wrappers such as:
public class ApiResponseCustomerDto
extends ApiResponse<CustomerDto> {
}and:
public class ApiResponsePageCustomerDto
extends ApiResponse<Page<CustomerDto>> {
}No duplicate openapi-generics.envelope declaration is required on the client when producer and codegen components are aligned.
The application-owned envelope must be available as a dependency of the generated client module.
BYOC allows generated clients to reuse DTOs already owned by your application or shared contract modules instead of generating duplicate models.
Configure mappings through additional properties:
<additionalProperties>
<additionalProperty>
openapi-generics.response-contract.CustomerDto=com.example.contract.CustomerDto
</additionalProperty>
<additionalProperty>
openapi-generics.response-contract.AddressDto=com.example.contract.AddressDto
</additionalProperty>
</additionalProperties>Each mapping follows:
openapi-generics.response-contract.<OpenAPI model name>=<fully-qualified Java type>
Mapped models are imported directly from the shared contract module.
Example:
public class ServiceResponseCustomerDto
extends ServiceResponse<CustomerDto> {
}where CustomerDto resolves to:
com.example.contract.CustomerDtoinstead of a generated duplicate.
BYOC remains a client-side mapping because it expresses which existing Java type should satisfy a particular OpenAPI model name in the generated source graph.
Application-defined generic containers are reconstructed from the metadata published in the OpenAPI document.
Producer configuration may define:
openapi-generics:
containers:
- type: com.example.contract.Paging
item-property: content
- type: com.example.contract.Window
item-property: itemsThe projected document preserves the container identity through metadata such as:
x-data-container: Window
x-data-container-type: com.example.contract.Window
x-data-item: CustomerDtoThe client generator uses x-data-container-type to reconstruct the Java container type without requiring container-specific client configuration.
Example generated wrapper:
public class ServiceResponseWindowCustomerDto
extends ServiceResponse<Window<CustomerDto>> {
}With BYOE:
public class ApiResponseWindowCustomerDto
extends ApiResponse<Window<CustomerDto>> {
}Application-defined container classes must be available on the generated client classpath.
Built-in and configured containers participate in the same reconstruction pipeline.
Built-in response shapes include:
ServiceResponse<T>
ServiceResponse<List<T>>
ServiceResponse<Set<T>>
ServiceResponse<Page<T>>BYOE envelopes participate in the same supported response-shape model.
Application-defined generic containers published through OpenAPI Generics metadata also participate in the same reconstruction pipeline when their Java types are available on the client classpath.
Representative combinations include:
ServiceResponse<Window<T>>
ApiResponse<Paging<T>>The effective Java type is reconstructed from the contract metadata carried by the input OpenAPI document.
OpenAPI Generics provides two levels of opt-out.
Set:
<openapi.generics.skip>true</openapi.generics.skip>This skips the template extraction, patching, and overlay lifecycle provided by openapi-generics-java-codegen-parent.
Use the upstream Java generator:
<generatorName>java</generatorName>This removes OpenAPI Generics reconstruction behavior entirely and returns generation ownership to standard OpenAPI Generator.
Use fallback mode for:
- troubleshooting
- output comparison
- isolating generator behavior
- temporary opt-out scenarios
After generation, verify the contract behavior that matters to the consuming module:
- generated wrappers extend the intended shared envelope instead of redefining it
- built-in or configured generic containers resolve to the intended Java contract types
- BYOC mappings reuse the configured external Java models instead of generating duplicates
- contract-owned infrastructure models are not regenerated
- generated sources compile against the required shared contract dependencies
Representative built-in wrapper:
public class ServiceResponsePageCustomerDto
extends ServiceResponse<Page<CustomerDto>> {
}Representative BYOE wrapper:
public class ApiResponseWindowCustomerDto
extends ApiResponse<Window<CustomerDto>> {
}Repository verification additionally covers metadata consumption, generated-source hygiene, standard Java RestClient transport compatibility, and the complete producer → OpenAPI → generated client → consumer lifecycle.
Generated wrappers are transport bindings, not application contracts.
Application code should depend on shared contract types such as:
ServiceResponse<CustomerDto>or:
ApiResponse<CustomerDto>rather than generated wrapper classes.
Generated classes such as:
ServiceResponseCustomerDtoexist to bind concrete OpenAPI response schemas to the generic Java contract during transport and deserialization.
Keep generated clients behind an adapter boundary when possible so application code remains independent of generated artifacts.
The contract owner should remain the shared Java contract module, not generated source.