| contract | ||
| docs | ||
| example-service | ||
| spec-generator | ||
| .gitignore | ||
| generate-spec.sh | ||
| pom.xml | ||
| README.md | ||
| settings.xml | ||
spec-from-skeleton
Write the skeleton, get the OpenAPI contract — no controllers required.
A small demo of a hybrid API workflow: you get the ergonomics of code-first (write plain Java interfaces + DTOs, let your IDE and compiler help you) but the output of contract-first (a real OpenAPI 3 document you can publish, review, and generate clients from) — without implementing a single controller to produce it.
The trick: Byte Buddy generates empty stub @RestControllers
at runtime from your annotated interfaces, so SpringDoc can
emit the spec as if the controllers existed.
Modules
| Module | What it is | Runs? |
|---|---|---|
contract |
The skeleton you hand-write: API interfaces (@RequestMapping, @GetMapping…) + DTOs (@Schema records). A plain library jar. |
no (library) |
spec-generator |
Reads contract, generates stub controllers with Byte Buddy, and serves the generated OpenAPI document + Swagger UI. No controllers written here. |
port 8080 |
example-service |
A real service that implements the same contract with hand-written controllers and in-memory data. Proves the skeleton is consumable both ways. |
port 8081 |
┌────────────────────┐
│ contract │ interfaces + DTOs (the skeleton)
└─────────┬──────────┘
│ depended on by
┌───────────┴───────────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ spec-generator │ │ example-service│
│ (Byte Buddy → │ │ (real @Rest- │
│ OpenAPI doc) │ │ Controllers) │
└────────────────┘ └────────────────┘
Requirements
- Java 21+
- Maven 3.8+
Maven repositories. The commands below pass
-s settings.xml, a checked-in settings file that resolves everything straight from Maven Central. This keeps the demo buildable anywhere — including behind a corporate network whose global~/.m2/settings.xmlmirrorscentralthrough an internal repository. Drop-s settings.xmlif your default setup already reaches Central.
Quick start
Build & test everything
mvn -s settings.xml clean install
1. Generate the OpenAPI document from the skeleton
./generate-spec.sh # boots spec-generator, writes openapi.json + openapi.yaml, stops
./generate-spec.sh --keep # same, but leaves the server (and Swagger UI) running
Or run the generator yourself and explore the live docs (after the build above):
java -jar spec-generator/target/spec-generator-*.jar
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/v3/api-docs
- OpenAPI YAML: http://localhost:8080/v3/api-docs.yaml
Note there is no controller code in spec-generator — only the generator and config.
2. Run the real service that implements the same contract
java -jar example-service/target/example-service-*.jar
curl http://localhost:8081/api/v1/users
curl -X POST http://localhost:8081/api/v1/users \
-H 'Content-Type: application/json' \
-d '{"lastName":"Doe","firstName":"Jane","email":"jane.doe@example.com","role":"TECHNICIAN"}'
curl "http://localhost:8081/api/v1/products?page=0&size=2"
How it works
At startup, ApiStubAutoConfiguration (a BeanDefinitionRegistryPostProcessor in
spec-generator):
- scans the
contractpackage for interfaces annotated with@RequestMapping; - for each, uses Byte Buddy to generate a class that implements the interface,
is annotated
@RestController, and re-declares the interface's type annotations (like@Tag) so SpringDoc treats it as a real controller; - registers each generated class as a Spring bean returning empty responses.
SpringDoc then walks those beans and produces the OpenAPI document from the
annotations on the interfaces and DTOs. See docs/how-it-works.md
for the detailed walkthrough.
The CQRS inner-interface pattern
UserApi is composed from two smaller interfaces so consumers can implement reads and
writes separately (or together):
public interface UserApi extends UserReadOperations, UserWriteOperations {}
- implement
UserApifor one controller that does everything (seeUserController); - or implement only
UserReadOperations/UserWriteOperationsto split queries from commands across separate controllers with separate security.
ProductApi shows the plain, single-interface case for contrast.
Technologies
- Spring Boot 3.2.1 · SpringDoc OpenAPI 2.3.0 · Byte Buddy 1.14.11 · Jakarta Validation
License
Apache 2.0