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, in Java (Maven) or Kotlin (Maven or Gradle Kotlin
DSL): a single apis/ directory at the repository root holding every contract the service exposes or consumes, and
the code-generation plugin executions (Maven) or tasks (Gradle) 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/(Maven) orbuild/generated/(Gradle), is git-ignored, and is excluded from Checkstyle, PMD, SpotBugs (Java) or detekt and ktlint (Kotlin), JaCoCo, and Sonar. If the generated code is wrong, the fix is in the spec or the generator configuration.
The language and build tool come from springboot-stack.yml’s `project.language (java or kotlin, absent means
java) and project.buildTool (maven or gradle, absent means maven; gradle is only valid with kotlin, and
a manifest pairing it with java makes the skill stop and report). The contracts in apis/ are identical in all
three combinations — a contract never depends on the language implementing it; only the generator changes:
| Contract | Kotlin + Maven | Kotlin + Gradle |
|---|---|---|
REST server |
|
Same options via the |
REST client |
|
Same libraries, |
gRPC |
The Java output plus |
|
GraphQL |
|
|
AVRO |
Unchanged — Java classes called from Kotlin |
|
HTML documentation |
Unchanged ( |
|
On Gradle every plugin, protoc artifact and library version goes in gradle/libs.versions.toml (resolved live,
protobuf and gRPC aligned to the versions the Spring Boot BOM manages), and modules apply plugins with
alias(libs.plugins.…) and no inline version, mirroring the Maven "no <version> in module poms" rule.
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 (or the Gradle equivalent) 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.
The skill announces whether it is creating the API layer from scratch or updating an existing one (listing the
contract files, documentation toolchains and generator executions that exist). For each pre-existing artifact it
owns it warns and asks Update (Recommended) / Keep as is / Replace, unless on-existing pre-answers.
Existing contract files are never rewritten by Update, since a contract is the service’s real API rather than a
template; Replace on a contract swaps a real API definition for a placeholder, so it is only offered after saying
so explicitly and is never recommended.
Plugin
Shipped by the iru-springboot plugin of the ai-catalog marketplace (install with /plugin install iru-springboot@ai-catalog; see Plugins).
Invocation
/iru-setup-java-springboot-apis
Optionally with args (stack-file:, mode:, on-existing: lines), as passed by iru-setup-java-springboot.
Inputs
| Argument | Required | Description | Default |
|---|---|---|---|
|
No |
|
|
|
No |
|
None |
|
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 |
|
Yes |
Every generator plugin and library version lives in the version catalog; missing entries are added there, never
inline in a module |
None — a precondition |
|
No |
Read from |
|
Outputs
-
A report stating whether the API layer was created from scratch or updated, the action taken per pre-existing artifact (created / updated / replaced / kept as is), and any gaps left in kept artifacts.
-
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 (Maven) or tasks (Gradle) added to
api/rest-server,api/grpc-server,api/graphql-server,infrastructure/clients/<name>,infrastructure/producers, andapi/consumers— including the HTML documentation executions. On Gradle, also thegradle/libs.versions.tomlentries and thedocumentation-group task names thatiru-setup-java-springboot-github-workflowsdiscovers. 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.
The generated files' bytes come from template files under templates/ (and, where a template needs variant-specific notes, reference/) in .claude/skills/iru-setup-java-springboot-apis/; the skill reads only the files for the variant it resolved, not the whole set, which keeps its SKILL.md small.
Execution flow
Dependencies
Invokes
None. It reads springboot-stack.yml and the module poms or build.gradle.kts files, and writes contracts and
generator configuration. It mentions iru-setup-java-springboot-pom (which must already have created the modules
and managed plugin versions) and iru-setup-java-springboot-github-workflows (which discovers the Gradle
documentation tasks) only as precondition and downstream consumer, not as calls.
Invoked by
-
Setup Java Spring Boot — Step 8, apis/ contracts and generators, via the
iru-isolated-skill-executoragent.
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.