OpenAPI 3.2 with openapi-processor
19. October 2025
OpenAPI 3.2 was released last month. See Upgrading from OpenAPI 3.1 to 3.2 for an overview of the changes.
The newest openapi-processor-spring/micronaut 2025.6 release adds support for OpenAPI 3.2.
This article will show use of two enhancements of OpenAPI 3.2 with openapi-processor-spring:
-
$self: it provides the base URI for resolving relative referencesIt is used here with the package-name-from-location feature and simplifies the
$refs in the OpenAPI description. -
itemSchema: which is part of the OpenAPI sequential and streaming media types support. A bit more detail can be found in the blog article JSON Streaming in OpenAPI v3.2.0.It is used to show two sample streaming apis using Spring
SseEmitterandStreamingResponseBodyand the required mapping.
the sample OpenAPI
Here is the sample OpenAPI
openapi: 3.2.0 (1)
$self: '../main/kotlin/io/openapiprocessor/samples/' (2)
info:
title: openapi-processor-spring stream sample api
version: 1.0.0
servers:
- url: "https://openapiprocessor.io/{path}"
variables:
path:
default: api
paths:
/sse:
$ref: 'sse/sse.yaml' (3)
/jsonl:
$ref: 'jsonl/jsonl.yaml' (3)
and the project directory layout.
sample
\---- src
+---- api
| +---- mapping.yaml
| \---- openapi.yaml
\---- main
+---- kotlin
| \---- io
| \---- openapiprocessor
| \---- samples
| +---- jsonl
| | +---- JsonlController.kt
| | \---- jsonl.yaml (4)
| +---- sse
| | +---- SseController.kt
| | \---- sse.yaml (4)
| \ StreamApplication.kt
\---- resources
\---- application.properties
| 1 | the new OpenAPI version |
| 2 | this sets the base uri of the openapi.yaml document used for resolving relative references. |
| 3 | the $self allows to shorten the $ref uris of the endpoints and avoids repeating the full relative path from the openapi.yaml entry document.
Without openapi.yaml
|
| 4 | the OpenAPI path items of the endpoints. Putting them in a source package makes it possible to use the package name of the location as package name for generated files.
package-name-from-location describes this in more detail. |
the streaming endpoints
We have two sample endpoints, one that provides a application/jsonl stream …
get:
tags:
- jsonl
summary: stream objects
description: endpoint has a stream response
parameters:
- name: source
description: query, string
in: query
schema:
type: string
responses:
'200':
description: jsonl stream
content:
application/jsonl:
itemSchema: (1)
type: object
properties:
foo:
type: string
and another one that provides server sent events:
get:
tags:
- sse
summary: stream objects
description: endpoint has a stream response
parameters:
- name: source
description: query, string
in: query
schema:
type: string
responses:
'200':
description: sse
content:
application/json:
itemSchema: (1)
type: object
properties:
foo:
type: string
| 1 | the itemSchema is a new keyword to describe the objects of the stream. |
mapping configuration
With Spring Boot (MVC) we will use SseEmitter as endpoint return type to send server sent events and StreamingResponseBody to send JSON lines.
To tell the processor to use those classes as result type instead of the itemSchema we use a new variant of the result mapping. Everything else in the mapping.yaml is not important for this, just look at the map: at the end :-)
openapi-processor-mapping: v15
options:
# shortcut to package-names.base
# package-name: io.openapiprocessor.samples
package-names:
# same as package-name
base: io.openapiprocessor.openapi
# this enables package generation from the endpoint $ref file location
location: io.openapiprocessor.samples
# target-dir options
target-dir:
# add src/resource directories to target-dir
layout: standard
# create a property file with server/url path (requires standard target-dir layout)
base-path:
server-url: 0
properties-name: api.properties
# generate javadoc from OpenAPI description properties
javadoc: true
# enable code/javadoc formatting
format-code: true
# generate java records
model-type: record
map:
paths:
/sse:
result: plain => org.springframework.web.servlet.mvc.method.annotation.SseEmitter (1)
/jsonl:
result: plain => org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody (1)
| 1 | the new result mapping: instead of providing a wrapper class with a single generic parameter, like ResponseEntity we use an arrow mapping:
which means, instead of the plain schema described in the OpenAPI response, use the |
generated code
After running the processor we will get this for the JSONL endpoint:
package io.openapiprocessor.samples.jsonl;
import io.openapiprocessor.openapi.support.Generated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@Generated(value = "openapi-processor-spring", version = "2025.5")
public interface JsonlApi {
/**
* stream objects endpoint has a stream response
*
* @return jsonl stream
*/
@GetMapping(path = "/jsonl", produces = {"application/jsonl"})
StreamingResponseBody getJsonl(@RequestParam(name = "source", required = false) String source); (1)
}
package io.openapiprocessor.samples.jsonl;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.openapi.support.Generated;
@Generated(value = "openapi-processor-spring", version = "2025.5")
public record JsonlGetResponse200(@JsonProperty("foo") String foo) {
}
Because the itemSchema is defined inline it gets a generated name, i.e. JsonlGetResponse200.
And this for the SSE endpoint:
package io.openapiprocessor.samples.sse;
import io.openapiprocessor.openapi.support.Generated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@Generated(value = "openapi-processor-spring", version = "2025.5")
public interface SseApi {
/**
* stream objects endpoint has a stream response
*
* @return sse
*/
@GetMapping(path = "/sse", produces = {"application/json"})
SseEmitter getSse(@RequestParam(name = "source", required = false) String source); (1)
}
package io.openapiprocessor.samples.sse;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openapiprocessor.openapi.support.Generated;
@Generated(value = "openapi-processor-spring", version = "2025.5")
public record SseGetResponse200(@JsonProperty("foo") String foo) {
}
Again, because the itemSchema is defined inline it gets a generated name, i.e. SseGetResponse200.
the sample project
The sample project is available in the samples repository. It has a simple implementation of the two endpoints (which you should not use as reference implementation).
The sample has two HTTP client scripts (Intellij IDEA) to test each endpoint.
summary
This article used two new OpenAPI 3.2 features, $self & ìtemSchema that openapi-processor does support with the latest version. $self is used to simplify the path item $ref s and itemSchema is used to describe the streaming payloads. With a result mapping the processor generated the endpoint methods with te mapped streaming result types.