Setup Java Spring Boot APIs
| This documentation was generated with the assistance of AI. Please report any inaccuracies. |
Sets up the contract-first API layer of a Spring Boot service: a single apis/ directory at the repository root
holding every contract the service exposes or consumes, and the code-generation plugin executions wired into
whichever module consumes each contract.
Purpose
iru-setup-java-springboot-apis enforces two invariants:
-
Every contract lives under
apis/at the repository root, never inside a module’ssrc/. One directory is where a reviewer looks to see everything the service exposes and consumes, and it keeps a contract from being coupled to the module that happens to implement it today.apis/is not a Maven module. -
Generated code is never committed and never hand-edited. It lands in each module’s
target/generated-sources/, is git-ignored, and is excluded from Checkstyle, PMD, SpotBugs, JaCoCo, and Sonar. If the generated code is wrong, the fix is in the spec or the generator configuration.
The setting that makes the REST generator safe to re-run is interfaceOnly=true: the generator owns the interfaces
and DTOs, and the hand-written @RestController implements them, so mvn generate-sources can never overwrite
real logic. Without it the generator emits controllers and every regeneration becomes a merge conflict.
Contracts are versioned in the filename and in the path inside the contract — an OpenAPI spec’s paths carry
/v1/…, a protobuf file declares package <service>.v1, an AVRO namespace ends in .v1, and a GraphQL SDL,
which has no in-band version at all, is versioned by filename and by the package its types generate into — so a
breaking change means a new v2 file alongside the v1 one, with both generating and both served until v1 is
retired.
apis/messaging/ is the one contract directory holding two kinds of file. The AVRO schemas define the message
payloads and are what avro-maven-plugin compiles into Java; an AsyncAPI specification alongside them defines
the interface — which topics exist, in which direction the service uses each one, which message each carries, and
on which broker. Nothing is generated from the AsyncAPI file except documentation, but without it the AVRO schemas
describe messages with no discoverable topics. Keeping both in one directory is deliberate: a new event means a new
.avsc and a new channel, and a reviewer should see both in the same diff.
HTML documentation is generated alongside the code — the OpenAPI html2 generator, protoc-gen-doc for protobuf,
spectaql (plus an optional GraphQL Voyager schema graph) for the GraphQL SDL, and @asyncapi/html-template for
messaging — into target/generated-docs/, where the build workflow publishes it to GitHub Pages next to the Antora
site, so the documentation pages link to it rather than duplicating the endpoint list by hand.
GraphQL needs that treatment explicitly because graphql-codegen-maven-plugin generates code, not documentation:
without a second toolchain the one contract-first API in the reactor would publish nothing. It follows the same
shape as messaging — an npm toolchain pinned beside the contract in apis/graphql-server/docs/, driven from the
Maven build by frontend-maven-plugin at generate-resources, with a graphql.docs.skip escape hatch for offline
builds. SpectaQL is the reference someone reads to integrate (oneFile: true yields one self-contained
index.html); Voyager is the interactive graph that answers how the types connect, and is optional because it
roughly doubles the published bytes. Both read the SDL directly, so neither needs a running service or an
introspection endpoint in CI.
The AsyncAPI documentation is produced by the official, non-Maven AsyncAPI CLI, driven from the Maven build via
frontend-maven-plugin so mvn generate-resources yields it with no globally installed Node. The specification is
written against the current AsyncAPI version (3.1.0 at the time of writing); the skill confirms what’s current
rather than hardcoding it, since asyncapi validate reports asyncapi-latest-version informationally whenever a
newer one exists. That same validation runs as its own execution ahead of generation, and it is a real gate rather
than decoration: it resolves and type-checks the referenced .avsc files, so a malformed document, a $ref to a
missing schema, or an .avsc that isn’t valid AVRO all fail the build instead of yielding a page with empty
payload tables. An out-of-date asyncapi: version, by contrast, is reported only as information — so a new
specification release can never break the build on its own.
Inputs
| Argument | Required | Description | Default |
|---|---|---|---|
|
No |
|
|
Target module directories |
Yes |
Each contract generates into a specific module. A missing module is reported and its contract skipped — this skill never creates a module. |
None — a precondition |
Root |
Yes |
The generator plugin versions must already be managed there. If one is missing it is added to the root pom, never inline in a module pom. |
None — a precondition |
Outputs
-
apis/README.mdexplaining the layout, which module each directory generates into, that generated code is never committed, how to regenerate, and the versioning convention. -
apis/rest-server/<service>-api-v1.yaml— an OpenAPI 3.1 starter spec with per-field descriptions, constraints, a documented error schema with4xx/5xxresponses, and asecuritySchemesentry if security is enabled. -
apis/grpc-server/<service>_v1.proto— a starter protobuf file with a unary and a server-streaming RPC, every message, field, and RPC commented, plus the field-numbering andreserveddiscipline stated in-file. -
apis/graphql-server/<service>-v1.graphqls— a starter SDL with a type, an enum, an input, a query and a mutation, every element carrying a description string, from whichgraphql-codegen-maven-plugingenerates resolver interfaces and model types. Two configuration details are load-bearing rather than cosmetic: the generator’s default validation annotation isjavax.validation.constraints.NotNull, which does not exist on Jakarta and fails compilation until overridden, and the SDL must additionally be copied onto the runtime classpath (target/classes/graphql/, viamaven-resources-plugin) because Spring for GraphQL loads the schema at startup fromclasspath:graphql/**/— a service missing that copy compiles cleanly and then refuses to start. -
apis/rest-client/<name>/,apis/grpc-client/<name>/andapis/graphql-client/<name>/— placeholder contracts per downstream service, each prominently marked as someone else’s contract to be replaced with the real published spec. These have two consumers, not one: the client generator, and the API mock in the compose stack that Setup Java Spring Boot Testcontainers builds from the very same file. Because it is theexamplesand not the schemas that become the mocked responses, every client operation gets at least one request/response example pair — named after the case it represents (found,not-found,rate-limited) and covering the error responses, since the adapter’s exception translation is what most needs testing. A spec with complete schemas and no examples generates a perfectly good client and a mock with nothing to say. gRPC contracts carry their examples in a companion artifact in the same directory, since protobuf has noexamplessection. -
apis/messaging/<event>-v1.avsc— AVRO schemas with adocon the record and every field, and a default on every optional field, since a field added without one is a backward-incompatible change the registry rejects. -
apis/messaging/<service>-async-api-v1.yaml— an AsyncAPI specification whose channeladdressvalues are the real topic names fromspring.cloud.stream.bindings..destination, and whose message payloads are$ref`s to the `.avscfiles rather than restatements of them. Each operation’sactionis written from this service’s point of view:sendis what it produces,receivewhat it consumes. (This is the ambiguity AsyncAPI 3 fixed — in a 2.x documentsubscribemeant what the service *sends, so an adapted older file is easy to invert, and an inverted action documents every arrow in the system backwards without anything failing to say so.) -
apis/messaging/docs/package.json(plus its committedpackage-lock.jsonand a.asyncapi-analyticsfile that keeps the CLI from reporting usage on every build) — the pinned AsyncAPI documentation toolchain. -
Generator executions added to
api/rest-server,api/grpc-server,api/graphql-server,infrastructure/clients/<name>,infrastructure/producers, andapi/consumers— including the HTML documentation executions. The AsyncAPI executions go in exactly one module (infrastructure/producersif it exists, otherwiseapi/consumers), since the specification describes the whole service and generating it twice would publish the same pages under two paths. -
Contract linting recommendations or wiring: Spectral/
oasdifffor OpenAPI,buf lint/buf breakingfor protobuf, GraphQL Inspector orgraphql-schema-linterfor the SDL, the Confluent schema-registry Maven plugin’stest-compatibilityfor AVRO, andasyncapi validatewired into the build (withasyncapi diffrecommended for breaking-change detection) for the AsyncAPI specification.
Execution flow
Dependencies
Invokes
None. It reads springboot-stack.yml and the module poms, and writes contracts and generator configuration.
Invoked by
-
Setup Java Spring Boot — Step 8, third of seven sub-skills, via the
iru-isolated-skill-executoragent. Runs after Setup Java Spring Boot Modules (generated sources need a module to land in, and the adapters written there consume the generated code) and before Setup Java Spring Boot Testcontainers (the compose stack must know whether a schema registry is needed).
Also runs standalone (/iru-setup-java-springboot-apis) to add a contract to an existing service.
Related agents
-
iru-gate-runner— spawned for Step 7’sgenerate-sourcesandcompileruns.
Source
SKILL.md on GitHub — the file this page was generated from.