Skip to content

Commit ce48fc9

Browse files
authored
feat: Support multiple OpenAPI specs per server (#97)
* feat: Add addSpec() builder method for multi-spec servers Introduces two addSpec() overloads on OpenApiServer.Builder: one accepting a Spec, handler map, and security-validator map, and one convenience form with no validators. The build() method is rewritten to either consume the bindings list (addSpec path) or synthesise a single binding from the legacy spec()/handlers()/securityValidator() fields. Mixing the two forms throws IllegalStateException. Cross-binding duplicate-basePath detection added.
1 parent a1f1d76 commit ce48fc9

12 files changed

Lines changed: 1745 additions & 52 deletions

File tree

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure function
1717
- [Highlights](#highlights)
1818
- [Maven artifact](#maven-artifact)
1919
- [Quick start](#quick-start)
20+
- [Multiple specs](#multiple-specs)
2021
- [Spec loading](#spec-loading)
2122
- [JSON mapping](#json-mapping)
2223
- [Body parsers and response writers](#body-parsers-and-response-writers)
@@ -160,6 +161,46 @@ public class YourServerLauncher {
160161
}
161162
```
162163

164+
### Multiple specs
165+
166+
A single server instance can host more than one OpenAPI spec — useful for running a v1/v2 API
167+
side-by-side, or a public and an internal admin surface in the same process. Each `addSpec()` call
168+
registers one spec and its handlers as an independent binding. The JDK `HttpServer` routes
169+
incoming requests to the binding whose `basePath` (the path component of `servers[0].url`) best
170+
matches the request URI.
171+
172+
`operationId`s and security-scheme names only need to be unique _within_ a single spec — two
173+
specs can each declare a `getCustomer` operation without conflict.
174+
175+
``` java
176+
OpenApiServer server = OpenApiServer.builder()
177+
.port(8080)
178+
.addSpec(v1Spec, Map.of(
179+
"getCustomer", new V1GetCustomer(),
180+
"listCustomers", new V1ListCustomers()))
181+
.addSpec(v2Spec, Map.of(
182+
"getCustomer", new V2GetCustomer(),
183+
"listCustomers", new V2ListCustomers(),
184+
"createCustomer", new V2CreateCustomer()))
185+
.build();
186+
```
187+
188+
Each spec that declares `securitySchemes` gets its own validator map via the three-argument
189+
overload:
190+
191+
``` java
192+
OpenApiServer.builder()
193+
.port(8080)
194+
.addSpec(v1Spec, v1Handlers, Map.of(
195+
"bearerAuth", (req, cred) -> jwt.verify(((BearerCredential) cred).token())))
196+
.addSpec(v2Spec, v2Handlers, Map.of(
197+
"apiKeyAuth", (req, cred) -> apiKeyStore.lookup(((ApiKeyCredential) cred).value())))
198+
.build();
199+
```
200+
201+
Mixing `addSpec()` with the legacy `spec()`/`handlers()`/`securityValidator()` methods in the
202+
same builder call is rejected at build time with `IllegalStateException`.
203+
163204
## Spec loading
164205

165206
`Spec.fromClasspath(Class<?>, String)` is the recommended way to load a spec packaged with your

0 commit comments

Comments
 (0)