Skip to content

Repository files navigation

OpenAPI Generics for Spring Boot — Keep Your API Contract Intact End-to-End

Build CodeQL codecov

Release Maven Central

Java Spring Boot OpenAPI Generator

License: MIT

Generics-Aware OpenAPI Contract Lifecycle

Prevent OpenAPI Generator from redefining your Java contract.
A contract-preserving OpenAPI Generator specialization for Java/Spring that keeps shared envelopes and DTOs reusable across service boundaries — no model explosion, no custom template fork to maintain.


Table of Contents


The problem in 30 seconds

A Spring Boot API may expose a strongly typed generic contract:

ResponseEntity<ServiceResponse<Page<CustomerDto>>> getCustomers() { ... }

That type carries more than a JSON shape.

ServiceResponse<T> is the response envelope, Page<T> is the pagination contract, and CustomerDto is the payload. Together, they form a Java contract that already has an owner.

A conventional OpenAPI client generation flow can flatten that contract into a new generated model:

// ❌ Generated by default
class ServiceResponsePageCustomerDto {
    PageCustomerDto data;
    Meta meta;
}

The generated type may represent a similar JSON structure, but the original contract identity has been lost. The envelope and container have become generated interpretations of types that already exist.

OpenAPI Generics preserves that identity:

// ✅ Generated with OpenAPI Generics
public class ServiceResponsePageCustomerDto
    extends ServiceResponse<Page<CustomerDto>> {}

The generated wrapper is intentionally thin. It binds a concrete OpenAPI response schema back to the shared generic contract instead of redefining that contract.

Default OpenAPI Generator
contract materialized as generated models
OpenAPI Generics
contract reconstructed from shared Java types

Why this matters

For a single endpoint, duplicated wrapper types may look harmless.

Across many generated clients, they accumulate. A BFF, aggregator, or downstream service can consume dozens of APIs, each introducing additional generated envelopes, container models, mapping layers, and opportunities for contract drift.

OpenAPI Generics keeps that ownership boundary explicit:

Shared Java contracts remain the authority. Generated clients provide transport bindings around them rather than becoming alternative contract definitions.

Payload models may still be generated. API clients and transport code remain generated by OpenAPI Generator. OpenAPI Generics specializes only the contract projection and reconstruction needed to preserve generic Java semantics across that boundary.

Define the contract once in Java, project its identity through OpenAPI, and reconstruct it deterministically in the generated client.


Get Started

OpenAPI Generics participates at two points in the contract lifecycle:

Java Producer Contract
        ↓
OpenAPI Projection
        ↓
Contract-Aware Client Reconstruction

1. See the full lifecycle

The repository contains runnable end-to-end samples covering:

  • Spring Boot 3 and Spring Boot 4 reference stacks
  • built-in ServiceResponse<T> contracts
  • BYOE envelopes
  • application-defined generic containers
  • BYOC reuse
  • transport compatibility

Each sample follows the same lifecycle:

Producer
    ↓
OpenAPI
    ↓
Generated Client
    ↓
Consumer

Start with samples/README.md for the runnable projects, Docker-based setup, and verification paths.


2. Add OpenAPI Generics to your project

OpenAPI Generics has separate producer-side and client-side integration points.

Producer — project Java contract semantics into OpenAPI

Add the server starter:

<dependency>
  <groupId>io.github.blueprint-platform</groupId>
  <artifactId>openapi-generics-server-starter</artifactId>
  <version>1.2.1</version>
</dependency>

The starter participates only when Springdoc generates the OpenAPI document.

It does not intercept application requests or change endpoint runtime behavior.

For the built-in ServiceResponse<T> contract, no envelope configuration is required. Applications using BYOE or application-defined generic containers can declare those contract types on the producer.

See Server-Side Adoption for the complete configuration model.

Client — reconstruct the Java contract during generation

Use the OpenAPI Generics codegen parent:

<parent>
  <groupId>io.github.blueprint-platform</groupId>
  <artifactId>openapi-generics-java-codegen-parent</artifactId>
  <version>1.2.1</version>
</parent>

Configure the official OpenAPI Generator Maven plugin as usual, but select the OpenAPI Generics Java specialization:

<generatorName>java-generics-contract</generatorName>

Normal OpenAPI Generator choices remain consumer-controlled, including the input specification, client library, package layout, and generator options.

The OpenAPI Generics integration prepares the reconstruction-specific template environment and restores the projected generic contract semantics without taking ownership of the ordinary OpenAPI Generator lifecycle.

See Client-Side Adoption for the complete generator configuration, BYOC mappings, fallback behavior, and generated-source setup.


Real-World Example

See the Licensing Project for a complete end-to-end BYOE example using a shared ApiResponse<T> contract.

The project demonstrates:

  • Spring Boot server integration with openapi-generics-server-starter
  • Java contract → OpenAPI projection
  • Generated Java client using openapi-generics-java-codegen-parent
  • Shared ApiResponse<T> reuse across service, client, SDK, and CLI
  • Docker-based end-to-end verification

What's New in 1.2.1

OpenAPI Generics 1.2.1 completes the contract-driven reconstruction metadata model by carrying Java envelope identity in the projected OpenAPI document through x-api-wrapper-type.

Together with the container identity metadata introduced in 1.2 through x-data-container-type, the document now carries the contract identity required by the Java generator to reconstruct both platform-owned and application-owned generic response contracts.

Java Contract
      ↓
OpenAPI Projection
      ↓
Envelope + Container Identity
      ↓
Generated Client Reconstruction

For aligned 1.2.1 producer and codegen components, the Java generator derives the envelope type from the OpenAPI document instead of requiring the same openapi-generics.envelope declaration on the client.

The same reconstruction model applies to built-in and application-owned contracts, for example:

ServiceResponse<Page<CustomerDto>>
ApiResponse<Window<CustomerDto>>

Highlights in 1.2.1 include:

  • contract-driven envelope reconstruction through x-api-wrapper-type
  • no duplicate client-side envelope configuration for aligned 1.2.1 components
  • end-to-end validation of application-defined containers with both built-in and BYOE envelopes
  • dedicated transport compatibility coverage for multipart, binary download, and form-urlencoded scenarios
  • clearer server-side failure diagnostics through a dedicated exception hierarchy
  • Spring Boot 3 and Spring Boot 4 reference-stack verification
  • OpenAPI Generator 7.24.0 verification

All 1.2.0 runtime contracts remain backward compatible.

No contract migration is required for existing 1.2 users.

For the complete release history, see the Changelog.


Key Features

Feature What it does Default
BYOE — Bring Your Own Envelope Reuse your existing response envelope (for example ApiResponse<T>) instead of ServiceResponse<T>. No migration required. ServiceResponse<T>
BYOC — Bring Your Own Contract Reuse your existing domain DTOs instead of generating duplicate models. Generate from spec
Application-defined containers Register your own generic container contracts (for example Paging<T> or Window<T>) and have them participate in the same projection, metadata, and reconstruction pipeline as built-in containers. Built-in containers only
Contract-driven envelope reconstruction Reconstruct the Java envelope directly from x-api-wrapper-type metadata without duplicating envelope configuration on the client. Enabled
Container-aware reconstruction Deterministically reconstruct built-in and configured generic container types from OpenAPI metadata instead of using container-specific generation logic. Enabled
Fallback to standard generation Opt out of generics-aware template patching, or switch fully back to stock OpenAPI Generator behavior with generatorName=java. Generics-aware generation enabled
Deterministic reconstruction Apply deterministic template patching, generated-source hygiene, and build-time validation to produce stable, contract-aligned Java clients. Enabled
End-to-end samples Complete producer → OpenAPI → generated client → consumer pipelines covering Spring Boot 3/4, ServiceResponse, BYOE, application-defined containers, and independent transport compatibility scenarios. See samples

BYOE — Bring Your Own Envelope

Already have an ApiResponse<T> or another response envelope shared across your services?

Use it as the contract source of truth without migrating to a platform-specific wrapper.

On the server/producer side:

openapi-generics:
  envelope:
    type: io.example.contract.ApiResponse

  # Optional: register application-defined generic containers
  containers:
    - type: io.example.contract.Paging
      item-property: content

    - type: io.example.contract.Window
      item-property: items

Key characteristics:

  • Your envelope remains the contract owner.
  • Generated wrappers extend your envelope instead of redefining it.
  • The envelope type must be available on the client classpath.
  • Springdoc-based projection is automatic.
  • Application-defined containers are optional and participate in the same projection and reconstruction model as built-in containers when configured.
  • The same metadata protocol can be represented directly in an OpenAPI document when interoperability requires it; Java-contract projection remains the primary architectural model.

BYOC — Bring Your Own Contract

Reuse DTOs you already own instead of generating duplicate models.

Map OpenAPI model names to existing Java types:

<additionalProperties>
  <additionalProperty>
    openapi-generics.response-contract.CustomerDto=io.example.contract.CustomerDto
  </additionalProperty>
</additionalProperties>

Each mapping follows:

openapi-generics.response-contract.<OpenAPI model name>=<fully-qualified Java type>

The generated client imports and reuses those contract types directly instead of producing duplicate DTO definitions.


Fallback to Standard Generation

Disable the generics-aware template patching with a single Maven property:

<openapi.generics.skip>true</openapi.generics.skip>

This skips the template extraction, patching, and overlay steps provided by openapi-generics-java-codegen-parent.

To fully revert to stock OpenAPI Generator behavior:

<generatorName>java</generatorName>

Use this mode for output comparison, troubleshooting, or temporary opt-out scenarios.


How it works

OpenAPI Generics preserves Java generic contract semantics across the OpenAPI lifecycle.

It keeps generic response envelopes, container payloads, and shared DTO contracts aligned from Spring Boot producers to generated Java clients.

The project is built on one principle:

The Java contract is the source of truth.
OpenAPI is a projection of that contract.
Client generation deterministically reconstructs the original contract.

Java Contract (SSOT)
        ↓
OpenAPI Projection
        ↓
Deterministic Client Reconstruction
        ↓
Contract-Aligned Client

In practice this means:

  • the response envelope remains a shared contract, not a generated artifact
  • generated wrapper classes extend existing contracts instead of redefining them
  • OpenAPI carries contract metadata, not contract ownership
  • container semantics are preserved through projection metadata
  • clients and servers remain aligned as contracts evolve

Projection and metadata paths

Java-contract projection is the primary architectural path. The same metadata protocol can also be represented directly in an OpenAPI document when integration with an externally managed specification requires it.

Springdoc-based (automatic)

The server starter discovers generic response contracts, projects wrapper schemas, enriches them with contract metadata, and marks infrastructure models so generated clients reconstruct the original Java contract instead of regenerating it.

Direct OpenAPI metadata (interoperability)

Externally managed OpenAPI documents can represent the reconstruction metadata directly through the OpenAPI Generics vendor extensions:

  • x-api-wrapper
  • x-api-wrapper-type
  • x-api-wrapper-datatype
  • x-data-container
  • x-data-container-type
  • x-data-item
  • x-ignore-model

Together, these extensions describe wrapper semantics, payload type, container identity, item type, and generation behavior. This provides an interoperability path for reconstruction without changing the project's primary Java-contract-as-authority model.

Architecture

OpenAPI Generics contract-first architecture flow

The architecture consists of two complementary phases:

  • Projection — derives deterministic OpenAPI metadata from Java contracts.
  • Reconstruction — restores contract-aligned Java client types during generation using the projected metadata.

Both phases share the same contract authority while keeping generated code isolated from application code.

For internal architecture and design decisions, see the architecture documentation.

Guarantees

  • ✔ Shared Java contracts remain the authority; generated wrappers bind concrete generic parameters instead of redefining the contract.
  • ✔ Envelope and container identity survive OpenAPI projection through explicit reconstruction metadata.
  • ✔ Built-in, BYOE, BYOC, and registered container semantics participate in the same deterministic Java reconstruction model.
  • ✔ Detected reconstruction-specific template drift and contract inconsistencies fail during generation rather than surfacing as silent runtime divergence.

Compatibility

OpenAPI Generics currently supports:

  • Java: 17+
  • Spring Boot: 3.4.x, 3.5.x, and 4.x
  • springdoc-openapi: 2.x with Spring Boot 3.x, and 3.x with Spring Boot 4.x
  • OpenAPI Generator: 7.x
  • Server integration: Spring WebMvc via springdoc-openapi-starter-webmvc-ui

The repository maintains verified Spring Boot 3, Spring Boot 4, and OpenAPI Generator reference baselines within these supported ranges.

For exact verified versions, the full compatibility matrix, and the support policy, see Compatibility & Support Policy.


Relationship to OpenAPI Generator

OpenAPI Generics is not a fork of OpenAPI Generator.

It builds on the upstream project as a focused Java/Spring specialization for preserving contract-owned generic structures across the OpenAPI lifecycle.

The generated OpenAPI document remains valid OpenAPI and can still be consumed by standard OpenAPI tooling; tools that do not understand OpenAPI Generics metadata simply ignore the vendor extensions.

OpenAPI Generics owns the specialization required for:

  • generic contract projection and OpenAPI Generics metadata
  • preservation of envelope and container identity
  • contract-aware Java wrapper reconstruction
  • BYOE and BYOC reconstruction semantics
  • reconstruction-specific template preparation, validation, and generated-source hygiene

OpenAPI Generator continues to own the ordinary generation concerns outside that specialization, including:

  • standard API and model generation
  • HTTP transport and client-library behavior
  • authentication generation
  • serialization behavior
  • upstream template structure and generator evolution

The project therefore consumes upstream template structure and applies a minimal generics-aware patch and overlay layer rather than maintaining a forked template set.

This boundary is deliberate: OpenAPI Generics reconstructs generic contract semantics while leaving ordinary OpenAPI generation behavior with the upstream ecosystem.

Generator Version Ownership

OpenAPI Generics does not lock consumers to a specific OpenAPI Generator version.

The provided parent configuration includes a tested default and defines the supported compatibility line, while consumers may override the openapi-generator.version property within the supported 7.x range.

The parent keeps the selected OpenAPI Generator version aligned across plugin execution, generator dependencies, and upstream template extraction so that the reconstruction pipeline uses a consistent toolchain.

OpenAPI Generics owns contract semantics and declares generator compatibility.
Consumers choose the OpenAPI Generator version they run within the supported compatibility line.


Modules

Module Responsibility
openapi-generics-contract Shared response contracts and platform-owned generic types.
openapi-generics-server-starter Spring Boot integration that projects generic contract metadata into OpenAPI.
openapi-generics-java-codegen OpenAPI Generator specialization that reconstructs contract-aligned Java clients.
openapi-generics-java-codegen-parent Maven parent for client generation, template patching, and generated-source hygiene.
openapi-generics-platform-bom Dependency alignment for OpenAPI Generics modules.

References

Project Documentation

  • Rationale: Why OpenAPI Generics Exists
    Explains the architectural motivation, design decisions, engineering trade-offs, and ecosystem constraints behind the project.

  • Documentation Site
    Official GitHub Pages documentation for adoption guides, architecture, compatibility, and project usage.

Articles

Standards & Specifications


Contributing

OpenAPI Generics is still evolving, and real-world feedback is the most valuable input for future improvements.

Whether you're evaluating the project, running it in a prototype, or using it in production, feedback is welcome.

Questions, bug reports, design discussions, and adoption experiences all help shape the roadmap.

For contributions and feedback:

  • 🐛 Bugs and issues → Issues
  • 💡 Design discussions and feature ideas → Discussions
  • 🔗 Private feedback → LinkedIn

If OpenAPI Generics helped solve a real problem in your environment, hearing about that experience is often just as valuable as a code contribution.


License

MIT — see LICENSE


Barış Saylı GitHub · Medium · LinkedIn

About

Prevent OpenAPI Generator from redefining your Java contract. Contract-preserving client generation for Spring Boot — available on Maven Central.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

25 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages