OpenAPI Generator
openapi-spec.The Problem with Manual Primary Adapters
Unless you work in microservices, a project usually has more than a few endpoints. As your domain grows, manually crafting primary adapters becomes a monumental task. It comes with a lot of drawbacks:
- Time-consuming: Every endpoint, every model, all by hand. It's a grind.
- Prone to human error: One typo, one missed field, and suddenly your API isn't doing what it's supposed to.
- Compromises documentation and maintainability: The moment your code deviates from your spec, your documentation is a lie, and your maintainability takes a nosedive.
The SIGEM project (a Grails monolith) boasts 76 controllers and a staggering 1102 endpoints.

Files Overview
- Java
- Kotlin
- Groovy
Generating Code
Let’s save us some problems by using one of the greatest libraries to ever exist: openapi-generator.
- Java
- Kotlin
- Groovy
You can find more information about the different configurations in the Documentation for the spring Generator.
You can find more information about the different configurations in the Documentation for the kotlin-spring Generator.
- You can find more information about the different configurations in the Documentation for the spring Generator.
- While a groovy generator exists, it's a client generator. For our use case (generating server-side code), it doesn't quite fit the bill. That's why we stick with the "spring" generator, which generates
.javaclasses.
inputspec must be pointing to the desired OpenAPI Specification YAML file
(src/main/resources/openapi.yaml).
Now that everything is set up, run the Build Task. When the task finishes, check the build\generated\sources\openapi folder. You’ll find the representation of the OpenAPI Specification (our contract) in classes, ready to be used.
Understand the Generated Code
- What’s Inside the Generated Code?
- Models: Java (or Kotlin) classes mirroring your OpenAPI schemas (e.g.,
Film). These include validation annotations, serialization logic, and builder patterns. - API Interfaces: Spring
@RestControllerinterfaces (e.g.,FilmApi) that define your endpoints and their method signatures.
- Models: Java (or Kotlin) classes mirroring your OpenAPI schemas (e.g.,
- Why Does It Look So Complicated? The generated code includes:
- Boilerplate for OpenAPI/Spring compatibility (e.g.,
@Validated,@Generatedannotations). - Validation logic (e.g.,
@NotNull,@Size) to enforce your contract. - Serialization/deserialization support (e.g., JSON ↔ Java (or Kotlin) object mapping).
- Boilerplate for OpenAPI/Spring compatibility (e.g.,
- Should I care? No.
- It’s autogenerated: Treat it like a compiled dependency. You use it, not modify it.
- Contract-first philosophy: The code exactly matches your OpenAPI spec. If you need changes, update the YAML file and regenerate.
- Maintenance-free: The generator handles updates, so you avoid manual refactoring.
Use the Generated Code
-
Make the
@RestControllerclass implement the generatedApiinterface:- Java
- Kotlin
- Groovy
-
Update the
Mapperinterface so it returns the generatedResponsemodel instead of the handwritten one:- Java
- Kotlin
- Groovy
-
Update the
@RestControllerAdviceclass so it returns the generatedErrormodel (which modelsProblemDetail) to keep consistency:- Java
- Kotlin
- Groovy
-
Delete handwritten
FilmResponse, it is no longer needed.
Build and run the application. Then go to http://localhost:8080/api/films/42
What's the Point of Generating Code?
Setting up this OpenAPI Generator might feel like performing ancient rituals just to get some code out. You might be thinking, "Wasn't this supposed to save me time?" And to that, I say: Yes, you're investing a little pain upfront to avoid a lot of pain later.
The OpenAPI Generator doesn't just spew out code; it acts as a very strict guardian of your API contract.
- No more manual DTO creation: Forget the mind-numbing task of writing Data Transfer Objects (DTOs) by hand. Your API specification defines them, and the generator builds them perfectly, every single time. This kills off an entire category of typos and inconsistencies.
- Trivial controller implementations: Your Spring
@RestControllerinterfaces, with all their routing annotations, are also generated. This means your controller classes transition from boilerplate factories to minimalist components that simply implement a predefined interface. - Compiler-enforced contract: Because your DTOs and controller interfaces are direct reflections of your OpenAPI spec, the compiler becomes your strictest ally. A missing field, a changed type, an altered endpoint signature, will result in a compile error. It is impossible to break your API contract without immediate feedback.
So, yeah, it's a bit of a setup, but the payoff? Absolutely worth it.