$ mn create-app my-openapi-app --features openapi
Table of Contents
OpenAPI/Swagger Support
Configuration to integrate Micronaut and OpenAPI/Swagger
Version: 6.13.2
1 Introduction
Micronaut includes support for producing OpenAPI (Swagger) YAML at compilation time. Micronaut will at compile time produce a OpenAPI 3.x compliant YAML file just based on the regular Micronaut annotations and the javadoc comments within your code.
You can customize the generated Swagger using the standard Swagger Annotations.
If you wish to generate Micronaut projects from OpenAPI definition files, utilize the OpenAPI Generator's Micronaut support. Refer to the "Micronaut server generation with OpenAPI" guide or the "Micronaut Client generation with OpenAPI" guide for details.
2 Release History
For this project, you can find a list of releases (with release notes) here:
3 Using the Micronaut CLI
To create a project with OpenAPI/Swagger support using the Micronaut CLI, supply the openapi
feature to the features
flag. For example:
This will create a project with the minimum necessary configuration for OpenAPI.
If you have already created a Micronaut project and will like to add Swagger support, you can simply follow instructions in subsequent sections.
4 Dependencies
To get started add Micronaut’s openapi
to the annotation processor scope of your build configuration:
annotationProcessor("io.micronaut.openapi:micronaut-openapi")
<annotationProcessorPaths>
<path>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi</artifactId>
</path>
</annotationProcessorPaths>
For Kotlin the |
To use the Swagger Annotations or Micronaut OpenAPI annotations add them to compile classpath
compileOnly("io.micronaut.openapi:micronaut-openapi-annotations")
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi-annotations</artifactId>
<scope>provided</scope>
</dependency>
Also, do not forget that for the correct operation of the annotation processor, the correct parameter
names in the controllers are required, therefore it is recommended that all libraries from which you plan
to add controllers be compiled with the
|
5 OpenAPI Definition
Once dependencies have been configured a minimum requirement is to add a @OpenAPIDefinition
annotation to your Application
class:
import io.micronaut.runtime.Micronaut;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.info.Contact
import io.swagger.v3.oas.annotations.info.Info
import io.swagger.v3.oas.annotations.info.License
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
class Application {
static void main(String[] args) {
Micronaut.run(Application)
}
}
import io.micronaut.runtime.Micronaut
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.info.Contact
import io.swagger.v3.oas.annotations.info.Info
import io.swagger.v3.oas.annotations.info.License
@OpenAPIDefinition(
info = Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = License(name = "Apache 2.0", url = "https://foo.bar"),
contact = Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
object Application {
@JvmStatic
fun main(args: Array<String>) {
Micronaut.run(Application.javaClass)
}
}
With that in place, you compile your project and a OpenAPI YAML file will be generated to the META-INF/swagger
directory of your project’s class output. For example, the above configuration generates:
-
For Java
build/classes/java/main/META-INF/swagger/hello-world-0.0.yml
-
For Kotlin
build/tmp/kapt3/classes/main/META-INF/swagger/hello-world-0.0.yml
The previously defined annotations will produce YAML like the following:
openapi: 3.0.1
info:
title: the title
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
6 OpenAPI Processing Options
It is possible to tweak the OpenAPI processing with system properties or with
a properties file. Options specified with system properties have priority over
those defined in the openapi.properties
file.
6.1 Configuring OpenAPI Processing with a properties file
You can specify OpenAPI processing configuration in a file located at the root level of your project directory. The expected filename is openapi.properties
.
It is possible to specify a different location and filename with the micronaut.openapi.config.file
System property.
micronaut.openapi.property.naming.strategy=KEBAB_CASE
micronaut.openapi.target.file=myspecfile.yml
...
..
.
Properties prefixed with micronaut.openapi.expand
will be expanded at compile time, for instance with:
micronaut.openapi.expand.api.version=v1.1
micronaut.openapi.expand.openapi.description=A nice API
The following example shows how to use the previous micronaut.openapi.expand
properties:
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "${api.version}",
description = "${openapi.description}",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
The generated specification file will look like:
openapi: 3.0.1
info:
title: Hello World
description: A nice API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "v1.1"
So, openapi.properties Property Resolution without prefix
|
6.2 Configuring OpenAPI Processing with system properties
It is possible to tweak the OpenAPI processing via system properties.
For instance in gradle:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs << '-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE'
...
}
or in gradle.properties
org.gradle.jvmargs=-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE
or in maven:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-J-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE</arg>
...
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
6.3 Configuring OpenAPI Processing with an application.yml file
It is possible to tweak the OpenAPI processing via standard way with micronaut environments (application.yml file).
micronaut:
openapi:
target:
file: myspecfile.yml
property:
naming:
strategy: KEBAB_CASE
Also, you can use properties from application.yml
file for placeholders.
my:
api:
version: 1.0.0
title: My title
api-description: My description
@OpenAPIDefinition(
info = @Info(
title = "${my.api.version}",
version = "${my.api.title}",
description = "${my.api.api-description}"
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
6.4 Available OpenAPI processing options
|
System property that enables or disables open api annotation processing. |
Default: |
|
System property that enables or disables OpenAPI 3.1.0 format. |
Default: |
|
System property that set JSON Schema Dialect for OpenAPI 3.1.0 format. |
Default: `` |
|
System property that enables or disables generation of a swagger (OpenAPI) specification file. This can be used whenever you already have a specification file and that you only need the Swagger UI. |
Default: |
|
System property that enables setting the open api config file. |
|
|
System property for server context path. |
|
|
System property for naming strategy. One jackson PropertyNamingStrategy. |
Default: |
|
System property for views specification. |
|
|
System property that enables setting the target file to write to. |
Default: |
|
System property that specifies the path where the generated UI elements will be located. |
Default: |
|
System property that specifies the location of additional swagger YAML and JSON files to read from. |
|
|
System property that specifies the location of current project. Need to set this property manually if you use kotlin (to save incremental annotation processing) or have some troubles with auto calculation project path. |
Default: calculated automatically |
|
System property that specifies the default security schema name, if it’s not specified by annotation SecurityScheme. |
|
|
If this property true, micronaut-openapi will process micronaut-security properties and annotations to construct openapi security schema. |
Default: |
|
System property that specifies the schema classes fields visibility level. By default, only public fields visible. |
Default: |
|
Is this property true, output file format will be JSON, otherwise YAML. |
Default: |
|
If this property is 'true', then the generated OpenAPI specification will include extensions for OpenAPI Generator and the generated client according to this specification will be much more accurate than without it. For example, enumerations will be described with extensions |
Default: |
|
The name of the result swagger file. |
Default: |
|
Active micronaut environments which will be used for @Requires annotations. |
|
|
Is this property true, properties wll be loaded in the standard way from application.yml. Also, environments from |
Default: |
|
Is this property true, micronaut-openapi will process micronaut-router versioning properties and annotations. |
Default: |
|
|
|
|
Final calculated openapi filenames. |
|
|
|
Default: |
|
Property that determines whether properties that have no view annotations are included in JSON serialization views. If enabled, non-annotated properties will be included; when disabled, they will be excluded. |
Default: |
|
Prefix for expandable properties. These properties can be used only for placeholders. |
|
|
Config file locations. By default, micronaut-openapi search config in standard path: |
|
|
If this property true, you can add some extra schemas to final openapi spec file. |
Default: |
|
System property to set schema duplicate resolution. Available values: |
Default: |
|
If this property true, generic separators and inner class separators in schema name will be empty string. |
Default: |
|
System property that set generic class separator for schema name generation. |
Default: |
|
System property that set inner class separator for schema name generation. |
Default: |
|
Properties prefix to set custom schema implementations for selected classes. |
|
|
Properties prefix to set schema name prefix or postfix by package. |
|
|
Properties prefix to set custom schema implementations for selected classes. |
|
|
Properties prefix to set schema name prefix or postfix by package. |
|
|
Properties prefix to set custom schema implementations for selected classes. |
7 Exposing Swagger Output
If you wish to expose the generated OpenAPI yaml output from your running application you can simply add the necessary static resource to the application configuration. For example:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
}
}
}
}
}
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/swagger/hello-world-0.0.yml
.
8 OpenAPI Generation for Controllers
By default, Micronaut will automatically at compile time build out the Swagger YAML definition from your defined controllers and methods. For example given the following class:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import reactor.core.publisher.Mono;
@Controller
public class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = MediaType.TEXT_PLAIN)
public Mono<String> index(String name) {
return Mono.just("Hello " + name + "!");
}
}
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import reactor.core.publisher.Mono
@Controller("/")
class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = MediaType.TEXT_PLAIN)
Mono<String> index(String name) {
return Single.just("Hello $name!")
}
}
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import reactor.core.publisher.Mono
@Controller("/")
open class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = [MediaType.TEXT_PLAIN])
open fun index(name: String): Mono<String> {
return Mono.just("Hello $name!")
}
}
The resulting output will be:
openapi: 3.0.1
info:
title: Hello World
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
paths:
/hello/{name}:
get:
description: ""
operationId: index
parameters:
- name: name
in: path
description: The person's name
required: true
schema:
type: string
responses:
200:
description: The greeting
content:
text/plain:
schema:
type: string
Notice how the javadoc comments are used to fill out the description of the API.
9 Naming Strategy
You can control how the Schema property names are dumped by setting the micronaut.openapi.property.naming.strategy
system property. It accepts one of
the following jackson's PropertyNamingStrategy
:
-
LOWER_CAMEL_CASE
-
UPPER_CAMEL_CASE
-
SNAKE_CASE
-
UPPER_SNAKE_CASE
-
LOWER_CASE
-
KEBAB_CASE
-
LOWER_DOT_CASE.
10 Custom serializers
You can set custom classes to create different open api schemas for selected classes:
micronaut.openapi.schema.org.somepackage.MyComplexType=java.lang.String
micronaut.openapi.schema.org.somepackage.MyComplexType2=java.lang.Integer
micronaut:
openapi:
schema:
org.somepackage.MyComplexType: java.lang.String
org.somepackage.MyComplexType2: java.lang.Integer
[micronaut]
[micronaut.openapi]
[micronaut.openapi.schema]
"org.somepackage.MyComplexType"="java.lang.String"
"org.somepackage.MyComplexType2"="java.lang.Integer"
micronaut {
openapi {
schema {
org.somepackage.MyComplexType = "java.lang.String"
org.somepackage.MyComplexType2 = "java.lang.Integer"
}
}
}
{
micronaut {
openapi {
schema {
"org.somepackage.MyComplexType" = "java.lang.String"
"org.somepackage.MyComplexType2" = "java.lang.Integer"
}
}
}
}
{
"micronaut": {
"openapi": {
"schema": {
"org.somepackage.MyComplexType": "java.lang.String",
"org.somepackage.MyComplexType2": "java.lang.Integer"
}
}
}
}
or by system properties:
-Dmicronaut.openapi.schema.org.somepackage.MyComplexType=java.lang.String -Dmicronaut.openapi.schema.org.somepackage.MyComplexType2=java.lang.Integer
or by openapi.properties
micronaut.openapi.schema.org.somepackage.MyComplexType=java.lang.String
micronaut.openapi.schema.org.somepackage.MyComplexType2=java.lang.Integer
Also, it can be used for replace classes schema with generics, for example, if you use jaxb generated classes and have custom serializer for JAXBElement class. And you can set custom schemas for different type args. For example if you have this classes structure:
package test.mypackage;
class MyDto {
public JAXBElement<? extends XmlElement> xmlElement;
public JAXBElement<? extends XmlElement2> xmlElement2;
public JAXBElement<? extends XmlElement3> xmlElement3;
}
class XmlElement {
public String propStr;
}
class XmlElement2 {
public String propStr2;
}
class XmlElement3 {
public String propStr3;
}
You can customize classes structure for openapi schema:
package io.micronaut.openapi;
// if you want to use generic from fields with type JAXBElement<T>
class MyJaxbElement<T> {
public String type;
public T value;
}
class MyJaxbElement2 {
public String type;
public List<String> values;
}
class MyJaxbElement3 {
public String type;
public String value;
}
And set openapi properties to map classes to custom openapi schema classes:
micronaut.openapi.schema.io.micronaut.openapi.JAXBElement=io.micronaut.openapi.MyJaxbElement
micronaut.openapi.schema.io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2>=io.micronaut.openapi.MyJaxbElement2
micronaut.openapi.schema.io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3>=io.micronaut.openapi.MyJaxbElement3
micronaut:
openapi:
schema:
io.micronaut.openapi.JAXBElement: io.micronaut.openapi.MyJaxbElement
io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2>: io.micronaut.openapi.MyJaxbElement2
io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3>: io.micronaut.openapi.MyJaxbElement3
[micronaut]
[micronaut.openapi]
[micronaut.openapi.schema]
"io.micronaut.openapi.JAXBElement"="io.micronaut.openapi.MyJaxbElement"
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2>"="io.micronaut.openapi.MyJaxbElement2"
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3>"="io.micronaut.openapi.MyJaxbElement3"
micronaut {
openapi {
schema {
io.micronaut.openapi.JAXBElement = "io.micronaut.openapi.MyJaxbElement"
io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2> = "io.micronaut.openapi.MyJaxbElement2"
io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3> = "io.micronaut.openapi.MyJaxbElement3"
}
}
}
{
micronaut {
openapi {
schema {
"io.micronaut.openapi.JAXBElement" = "io.micronaut.openapi.MyJaxbElement"
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2>" = "io.micronaut.openapi.MyJaxbElement2"
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3>" = "io.micronaut.openapi.MyJaxbElement3"
}
}
}
}
{
"micronaut": {
"openapi": {
"schema": {
"io.micronaut.openapi.JAXBElement": "io.micronaut.openapi.MyJaxbElement",
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement2>": "io.micronaut.openapi.MyJaxbElement2",
"io.micronaut.openapi.JAXBElement<test.mypackage.XmlElement3>": "io.micronaut.openapi.MyJaxbElement3"
}
}
}
}
After changing these settings, a complete recompilation of the project is necessary to ensure that the new settings are applied correctly. |
11 Schema decorators
If you have some classes with same names in different packages you can set postfix like this:
micronaut.openapi.schema-postfix.org.api.v1_0_0=100
micronaut.openapi.schema-postfix.org.api.v2_0_0=200
micronaut:
openapi:
schema-postfix:
org.api.v1_0_0: 1_0_0
org.api.v2_0_0: 2_0_0
[micronaut]
[micronaut.openapi]
[micronaut.openapi.schema-postfix]
"org.api.v1_0_0"=100
"org.api.v2_0_0"=200
micronaut {
openapi {
schemaPostfix {
org.api.v1_0_0 = 100
org.api.v2_0_0 = 200
}
}
}
{
micronaut {
openapi {
schema-postfix {
"org.api.v1_0_0" = 100
"org.api.v2_0_0" = 200
}
}
}
}
{
"micronaut": {
"openapi": {
"schema-postfix": {
"org.api.v1_0_0": 100,
"org.api.v2_0_0": 200
}
}
}
}
or by system properties:
-Dmicronaut.openapi.schema-postfix.org.api.v1_0_0=1_0_0 -Dmicronaut.openapi.schema-postfix.org.api.v2_0_0=2_0_0
or by openapi.properties
micronaut.openapi.schema-postfix.org.api.v1_0_0=1_0_0
micronaut.openapi.schema-postfix.org.api.v2_0_0=2_0_0
After changing these settings, a complete recompilation of the project is necessary to ensure that the new settings are applied correctly. |
12 OpenAPI Generator extensions
Micronaut OpenAPI can add extensions to the generated specification to produce more accurate code generation when using OpenAPI Generator. To enable the addition of extensions to the final OpenAPI specification, you need to add the following property:
micronaut.openapi.generator.extensions.enabled=true
Currently, the following extensions are supported:
Extension | Scope | Description |
---|---|---|
|
|
Text from the javadoc with tag |
|
|
List of enum items names from code. Generated enum items will be with same names |
|
|
List of javadocs from enum item elements |
|
|
List of deprecated enum items |
|
|
If the variable/parameter has a primitive type that is not in the OpenAPI 3 specification, this field will be filled in. Used for primitive java types |
|
|
If the variable/parameter has a type that is not in the OpenAPI 3 specification, this field will be filled in. Used for java types |
|
|
Message from |
|
|
Message from |
|
|
Message from |
|
|
Message from |
|
|
Message from |
13 Kotlin specific
To support incremental annotation processing, you need to explicitly set the path to
the project directory through the annotation processor setting micronaut.openapi.project.dir
like this:
kapt {
arguments {
arg("micronaut.openapi.project.dir", projectDir.toString())
}
}
If you use KSP need to disable incremental processing: ksp.incremental=false |
14 Swagger Annotations
You can take full control by augmenting your definition with Swagger Annotations. Swagger annotations take precedence over javadoc.
Add the Micronaut OpenAPI annotations to compile classpath
compileOnly("io.micronaut.openapi:micronaut-openapi-annotations")
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi-annotations</artifactId>
<scope>provided</scope>
</dependency>
and then annotate your controllers:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import reactor.core.publisher.Mono;
@Controller
public class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri="/greetings/{name}", produces= MediaType.TEXT_PLAIN)
@Operation(summary = "Greets a person",
description = "A friendly greeting is returned"
)
@ApiResponse(
content = @Content(mediaType = "text/plain",
schema = @Schema(type="string"))
)
@ApiResponse(responseCode = "400", description = "Invalid Name Supplied")
@ApiResponse(responseCode = "404", description = "Person not found")
@Tag(name = "greeting")
public Mono<String> greetings(@Parameter(description="The name of the person") @NotBlank String name) {
return Mono.just("Hello " + name + ", How are you doing?");
}
}
@Controller("/")
class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri="/greetings/{name}", produces= MediaType.TEXT_PLAIN)
@Operation(summary = "Greets a person",
description = "A friendly greeting is returned"
)
@ApiResponse(
content = @Content(mediaType = "text/plain",
schema = @Schema(type="string"))
)
@ApiResponse(responseCode = "400", description = "Invalid Name Supplied")
@ApiResponse(responseCode = "404", description = "Person not found")
@Tag(name = "greeting")
Mono<String> greetings(@Parameter(description="The name of the person") @NotBlank String name) {
return Mono.just("Hello $name, How are you doing?")
}
}
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import reactor.core.publisher.Mono
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
@Controller("/")
open class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri = "/greetings/{name}", produces = [MediaType.TEXT_PLAIN])
@Operation(summary = "Greets a person", description = "A friendly greeting is returned")
@ApiResponse(content = [Content(mediaType = "text/plain", schema = Schema(type = "string"))])
@ApiResponse(responseCode = "400", description = "Invalid Name Supplied")
@ApiResponse(responseCode = "404", description = "Person not found")
@Tag(name = "greeting")
open fun greetings(name: String): Mono<String> {
return Mono.just("Hello $name, how are you doing?")
}
}
The resulting output will be:
openapi: 3.0.1
info:
title: Hello World
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
paths:
/greetings/{name}:
get:
tags:
- greeting
summary: Greets a person
description: A friendly greeting is returned
operationId: greetings
parameters:
- name: name
in: path
description: The name of the person
required: true
schema:
minLength: 1
type: string
responses:
200:
description: OK
content:
text/plain:
schema:
type: string
400:
description: Invalid Name Supplied
404:
description: Person not found
14.1 Schemas and POJOs
If you return types are not simple strings and primitive types then Micronaut will attempt to generate a Schema definition. You can customize the
generation of the Schema by using the @Schema
annotation on your POJO. For example:
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name="MyPet", description="Pet description") (1)
class Pet {
private PetType type;
private int age;
private String name;
public void setAge(int a) {
age = a;
}
/**
* The age
*/
@Schema(description="Pet age", maximum="20") (2)
public int getAge() {
return age;
}
public void setName(String n) {
name = n;
}
@Schema(description="Pet name", maxLength=20)
public String getName() {
return name;
}
public void setType(PetType t) {
type = t;
}
public PetType getType() {
return type;
}
}
enum PetType {
DOG, CAT
}
1 | The @Schema annotation is used to customize the name of the schema |
2 | Properties can be customized too. |
14.2 Schemas and Meta Annotations
If you don’t have control of the source code and don’t want to have to annotate each parameter with @Schema
then it can be convenient to instead use a meta annotation.
For example if the aforementioned Pet
class cannot be annotated with @Schema
you can define a meta annotation:
@Documented
@Retention(RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Schema(name = "MyPet", description = "Pet description")
@interface MyAnn {
}
Then whenever Pet
is used as a parameter you can annotate the parameter with @MyAnn
.
14.3 Schemas and Generics
If a method return type includes generics then these will be included when calculating the schema name. For example the following:
class Response<T> {
private T r;
public T getResult() {
return r;
}
public void setResult(T r) {
this.r = r;
}
}
@Controller("/")
class MyController {
@Put("/")
public Response<Pet> updatePet(Pet pet) {
...
}
}
Will result in a schema called #/components/schemas/Response<Pet>
being generated. If you wish to alter the name of the schema you can do so with the @Schema
annotation:
@Put("/")
@Schema(name = "ResponseOfPet")
public Response<Pet> updatePet(Pet pet) {
...
}
In the above case the generated schema will be named #/components/schemas/ResponseOfPet
.
14.4 Schemas naming
By default, Micronaut uses Class simple name for Custom type schemas. Micronaut use simple name no matter if @Schema
annotation is defined on type or on property (getter). That means that if you have two properties of same type with @Schema
annotation without name
set, Micronaut will accidentally override one definition with another. So in that case you should
set name
on @Schema
annotation.
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "A pet") (1)
class Pet {
}
class Owner {
private Pet bird;
private Pet cat;
private Pet dog;
@Schema(description = "Pet that is a a bird") (2)
public Pet getBird() {
return bird;
}
@Schema(description = "Pet that is a cat") (3)
public Pet getCat() {
return cat;
}
@Schema(name = "Dog", description = "Pet that is a dog") (4)
public Pet getDog() {
return cat;
}
}
1 | Micronaut will generate schema with name Pet |
2 | Micronaut will generate schema with name Pet since name is not set, this will conflict with <1> and <3>, final Pet schema might be incorrect |
3 | Micronaut will generate schema with name Pet since name is not set, this will conflict with <1> and <2>, final Pet schema might be incorrect |
4 | Micronaut will generate schema with unique name Dog since name is set, there is no conflict, schema is correctly generated |
14.5 Schemas Annotation resolution
You can apply @Schema
annotation to type or property. But it’s important to note, that Micronaut will prioritize @Schema
on property over @Schema
on type.
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description="Pet") (1)
class Pet {
}
class Owner {
private Pet cat;
private Pet dog;
public Pet getCat() { (2)
return cat;
}
@Schema(name="MyPet", description="This is my pet") (3)
public Pet getDog() {
return dog;
}
}
1 | Micronaut will detect this annotation |
2 | Micronaut will use annotation <1> from type since there is none on property |
3 | Micronaut will use this annotation even if there exists one on Pet type |
15 Exposing Endpoints
It is possible to expose management Endpoints in the openapi specification file.
15.1 Enable Endpoints
To process user defined endpoints simply add:
endpoints.enabled=true ... .. .
15.2 Endpoints Tags
You can also provide some tags for all endpoints with the endpoints.tags=<comma separated list of tags>
flag, for instance:
endpoints.tags=Management Endpoints
15.3 Micronaut Built-In Endpoints
To enable the processing of built-in endpoints (https://docs.micronaut.io/latest/guide/index.html#providedEndpoints), you have to expose micronaut-management
as annotation processor and runtime dependency:
annotationProcessor("io.micronaut:micronaut-management")
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
</path>
</annotationProcessorPaths>
implementation("io.micronaut:micronaut-management")
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
</dependency>
and declare them in the openapi.properties file:
endpoints.enabled=true
endpoints.tags=Management Endpoints
endpoints.routes.class=io.micronaut.management.endpoint.routes.RoutesEndpoint
endpoints.beans.class=io.micronaut.management.endpoint.beans.BeansEndpoint
endpoints.health.class=io.micronaut.management.endpoint.health.HealthEndpoint
endpoints.loggers.class=io.micronaut.management.endpoint.loggers.LoggersEndpoint
endpoints.refresh.class=io.micronaut.management.endpoint.refresh.RefreshEndpoint
The syntax is the following: endpoints.<name>.class=<full class name of the endpoint>
where name
is an arbitrary name.
You can also add some tags, servers and security requirements to each endpoint:
endpoints.refresh.class=io.micronaut.management.endpoint.refresh.RefreshEndpoint
endpoints.refresh.servers=[{"url": "https://staging.gigantic-server.com/v1", "description": "Staging server"}]
endpoints.refresh.security-requirements=[{"petstore_auth": ["write:pets", "read:pets"]}]
15.4 Endpoints Servers
You can also provide some servers for all endpoints with the endpoints.servers=<json array of io.swagger.v3.oas.models.servers.Server>
flag, for instance:
endpoints.servers=[ \
{ \
"url": "https://{username}.gigantic-server.com:{port}/{basePath}", \
"description": "The production API server", \
"variables": { \
"username": { \
"default": "demo", \
"description": "this value is assigned by the service provider, in this example `gigantic-server.com`" \
}, \
"port": { \
"enum": [ \
"8443", \
"443" \
], \
"default": "8443" \
}, \
"basePath": { \
"default": "v2" \
} \
} \
} \
]
15.5 Endpoints Security Requirements
You can also provide some security requirements for all endpoints with the endpoints.security-requirements=<json array of io.swagger.v3.oas.models.security.SecurityRequirement>
flag, for instance:
endpoints.security-requirements=[{"api_key": []}]
...
..
.
Don’t forget to declare the referenced SecurityScheme
.
15.6 Endpoints Path
If you are using a custom path for your endpoints use endpoints.path
to set it:
endpoints.path=/endpoints
...
..
.
16 Micronaut OpenAPI annotations
Several annotations (OpenAPIDecorator OpenAPIGroup OpenAPIGroupInfo OpenAPIInclude OpenAPIManagement) OpenAPISecurity are available to enhance the generated OpenAPI.
To use them add Micronaut’s openapi
to to compile classpath of your application:
compileOnly("io.micronaut.openapi:micronaut-openapi-annotations")
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi-annotations</artifactId>
<scope>provided</scope>
</dependency>
16.1 @OpenAPIDecorator
The annotation can be used to add suffix and prefix for operationIds. This solves the problem when you have several different controllers, but with same operation names.
For example, when you have 2 controllers with same operations, but use generics:
@OpenAPIDecorator(opIdPrefix = "cats-", opIdSuffix = "-suffix")
@Controller("/cats")
interface MyCatsOperations extends Api<MyRequest, MyResponse> {
}
@OpenAPIDecorator("dogs-")
@Controller("/dogs")
interface MyDogsOperations extends Api<MyRequest, MyResponse> {
}
16.2 @OpenAPIExtraSchema
You can add extra schemas (unused in endpoints) to final specification file. To add unused schemas to openapi file
you can use @OpenAPIExtraSchema
annotaion.
To add current class to openapi file just add @OpenAPIExtraSchema
annotation without any parameters:
@OpenAPIExtraSchema
class UnusedSchema {
public String field1;
}
Another way, you can set classnames or packages to include / exclude extra schemas from final openapi file:
@OpenAPIExtraSchema(
// classes to add
classes = UnusedModel1.class,
// excluded classes, which marked with `@OpenAPIExtraSchema` annotation
excludeClasses = ExcludedModel.class,
// exclude classes by packages
excludePackages = "io.micronaut.openapi.exclude",
// include classes by packages
packages = "io.micronaut.openapi.extra"
)
class Application {
}
16.3 @OpenAPIGroup
You can divide your API description into several separate files using the OpenAPIGroup annotation. With this annotation, you can specify one or more groups that this endpoint will be included in, as well as specify groups from which this endpoint should be excluded. You can annotate method, class or package (subpackages not included!).
For example, you have a controller like this:
@Controller
public class ApiController {
@OpenAPIGroup(exclude = "v2")
@Get("/read/{id}")
public String read(String id) {
return "OK!";
}
@OpenAPIGroup("v2")
@Post("/save/{id}")
public String save2(String id, Object body) {
return "OK!";
}
@OpenAPIGroup({"v1", "v2"})
@Post("/save")
public String save(Object body) {
return "OK!";
}
}
After processing, you will have 2 different swagger files. The first file for group v1
contain the description of the endpoints /save
and /read/{id}
. The second one will contain
the description of the endpoints /save
and /save/{id}
.
You can read more about groups here: Versions and groups
16.4 @OpenAPIGroupInfo
If you need to describe a particular group with a separate @OpenAPIDefinition
annotation,
then add the OpenAPIGroupInfo annotation, in which specify the names of the group
to which this OpenAPI description belongs. This way you can generate as many
different swagger files as you want from one project.
An annotation can be added at the class level or at the package level.
@OpenAPIGroupInfo(
names = "v1",
info = @OpenAPIDefinition(
info = @Info(
title = "Public api v1",
version = "v1",
description = "This is API version 1",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
)
@OpenAPIGroupInfo(
names = "v2",
info = @OpenAPIDefinition(
info = @Info(
title = "Public api v2",
version = "v2",
description = "This is API version 2",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
)
@OpenAPIDefinition(
info = @Info(
title = "Private api",
version = "${service.version}",
description = "This is API version 2",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
16.5 @OpenAPIInclude
You can use OpenAPIInclude you can include additional Controller or Endpoint already compiled classes in the OpenAPI processing.
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "${service.version}",
description = "${openapi.description}",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
@OpenAPIInclude(
classes = {
io.micronaut.security.endpoints.LoginController.class,
io.micronaut.security.endpoints.LogoutController.class
},
tags = @Tag(name = "Security")
)
@OpenAPIInclude(
classes = io.micronaut.management.endpoint.env.EnvironmentEndpoint.class,
tags = @Tag(name = "Management"),
security = @SecurityRequirement(name = "BEARER", scopes = {"ADMIN"})
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
16.6 @OpenAPIManagement
OpenAPIManagement adds management endpoints.
OpenAPIManagement is mapped to:
@OpenAPIInclude(classes = {
io.micronaut.management.endpoint.beans.BeansEndpoint.class,
io.micronaut.management.endpoint.env.EnvironmentEndpoint.class,
io.micronaut.management.endpoint.health.HealthEndpoint.class,
io.micronaut.management.endpoint.info.InfoEndpoint.class,
io.micronaut.management.endpoint.loggers.LoggersEndpoint.class,
io.micronaut.management.endpoint.refresh.RefreshEndpoint.class,
io.micronaut.management.endpoint.routes.RoutesEndpoint.class,
io.micronaut.management.endpoint.stop.ServerStopEndpoint.class,
io.micronaut.management.endpoint.threads.ThreadDumpEndpoint.class
})
16.7 @OpenAPISecurity
OpenAPISecurity adds security endpoints.
It is mapped to:
@OpenAPIInclude(classes = {
io.micronaut.security.endpoints.LoginController.class,
io.micronaut.security.endpoints.LogoutController.class
})
16.8 @AccessorsStyle
You can use @AccessorsStyle to define your custom getters and setters if they are not the default get
and set
.
This is useful when defining getters and setters in a "fluent" way or when using Lombok for that:
@Introspected
@AccessorsStyle(readPrefixes = "", writePrefixes = "") (1)
class Person {
private String name;
private Integer debtValue;
private Integer totalGoals;
Person(String name, Integer debtValue, Integer totalGoals) {
this.name = name;
this.debtValue = debtValue;
this.totalGoals = totalGoals;
}
public String name() { (2)
return name;
}
public Integer debtValue() {
return debtValue;
}
public Integer totalGoals() {
return totalGoals;
}
public void name(String name) { (2)
this.name = name;
}
public void debtValue(Integer debtValue) {
this.debtValue = debtValue;
}
public void totalGoals(Integer totalGoals) {
this.totalGoals = totalGoals;
}
}
1 | Use @AccessorsStyle to configure the custom prefixes. In this case no prefix. |
2 | Define getters and setters without prefixes. |
Using @AccessorsStyle
will tell Micronaut how to access getters and setters and will also generate the appropriate Open API spec.
17 Merging Schemas
Often times you might want to generate OpenAPI (Swagger) YAML for built-in endpoints or paths from some other modules, such as security
. In order to generate YAML including all that information, Micronaut supports merging of multiple OpenAPI YAML files. So, you can create OpenAPI YAML files manually at some predefined path from where the information will then be merged into the final YAML file.
For example, if you are using Micronaut’s Security OpenID Connect with Amazon Cognito your application exposes several endpoints which you could define in an external OpenAPI YAML file such as:
openapi: 3.0.1
info:
title: OAuth
description: Endpoints related to the integration with Amazon Cognito
version: "1.0"
paths:
/logout:
get:
tags:
- security
description: deletes the JWT cookie and redirects to /oauth/login/cognito
operationId: logout
responses:
200:
description: logout 200 response
content:
application/json:
schema:
$ref: '#/components/schemas/Object'
302:
description: Found
links:
oauth-login-cognito:
operationId: oauth-login-cognito
/oauth/callback/cognito:
get:
tags:
- security
description: receives a callback from the authorization server with a code to
exchange it for an access and id token
externalDocs:
description: Amazon Cognito Token Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html
operationId: oauth-callback-cognito
parameters:
- name: authorizationResponse
in: query
required: true
explode: true
schema:
$ref: '#/components/schemas/AuthorizationResponse'
responses:
303:
description: redirects to home page upon successful completion of the authorization
code grant flow
headers:
Set-Cookie:
description: Cookied named JWT with the id token obtained from the authorization
server as the value
schema:
type: string
/oauth/login/cognito:
get:
tags:
- security
description: redirects to authorization server sign in page
externalDocs:
description: Amazon Cognito Authorization Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
operationId: oauth-login-cognito
responses:
302:
description: redirects to authorization server sign in page
/oauth/logout:
get:
tags:
- security
description: ends the session in the authorization server and the redirects
to /logout
externalDocs:
description: Amazon Cognito Logout Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
operationId: oauth-logout
responses:
302:
description: Found
links:
logout:
operationId: logout
components:
schemas:
AuthorizationResponse:
required:
- code
- state
type: object
properties:
code:
type: string
description: an authorization code which the OAuth 2.0 client can exchange
for an access token
nonce:
type: string
nullable: true
state:
type: string
Object:
type: object
You could also have a yaml file describing the endpoint which exposes the generated OpenAPI YAML file:
openapi: 3.0.1
info:
title: swagger
version: "1.0"
paths:
/swagger/demo-0.0.yml:
get:
tags:
- openapi
description: returns the OpenAPI YAML file describing the API
operationId: swagger
responses:
"200":
description: OpenAPI YAML file describing the API
content:
text/plain: {}
To merge both files with the generated OpenAPI definition point, Micronaut searches for additional OpenAPI yaml files in the openapi
folder, which is specified by micronaut.openapi.additional.files
.
micronaut.openapi.additional.files=openapi
Micronaut includes the endpoints defined in those files in the generated output.
18 Generating OpenAPI Views
Micronaut can generate views for your generated OpenApi specification. Currently, Swagger-ui, Redoc, OpenAPI Explorer and RapiDoc are supported. You can also use RapiPdf to generate a PDF from your spec file.
You can enable multiple views generation in a single application.
The resources needed to render the views (javascript, css, …) are loaded from CDNs: unpkg.com and fonts.googleapis.com.
18.1 Mapping Path
The path from where the swagger specification will be served by the http server defaults to swagger
. You can change it via the mapping.path
property.
Thus, by default, the views expect to find the yaml
under /swagger
.
If you change this mapping to something else:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swaggerYAML/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swaggerYAML/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swaggerYAML/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swaggerYAML/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swaggerYAML/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swaggerYAML/**"
}
}
}
}
}
You will need to set the mapping.path
property accordingly: micronaut.openapi.views.spec=mapping.path=swaggerYAML…
.
18.2 Enable Views Generation with a properties file
By default, the generation of views is disabled. You can enable views generation with a configuration properties file.
micronaut.openapi.views.spec = swagger-ui.enabled=true,\
redoc.enabled=true, \
openapi-explorer.enabled=true, \
rapidoc.enabled=true, \
rapidoc.bg-color=#14191f, \
rapidoc.text-color=#aec2e0, \
rapidoc.sort-endpoints-by=method
18.3 Enable Views Generation with system properties
By default, the generation of views is disabled.
To turn it on you have to set the following system property micronaut.openapi.views.spec
.
The string syntax is a series of comma-separated key-value pairs, to enable and configure the views.
-Dmicronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop
For instance in Gradle for Kotlin projects:
JAVA_TOOL_OPTIONS=-Dmicronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop \
./gradlew --no-daemon clean assemble
or in gradle.properties:
org.gradle.jvmargs=-Dmicronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop
or in build.gradle as well:
kapt {
arguments {
arg("micronaut.openapi.views.spec", "redoc.enabled=true,rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop")
}
}
or in Gradle for Java projects:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs << '-Dmicronaut.openapi.views.spec=rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop'
...
}
or in Gradle for Groovy projects:
tasks.withType(GroovyCompile) {
groovyOptions.forkOptions.jvmArgs.add('-Dgroovy.parameters=true')
groovyOptions.forkOptions.jvmArgs.add('-Dmicronaut.openapi.views.spec=rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop')
...
}
or in Maven:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-Amicronaut.openapi.views.spec=rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop</arg>
...
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
or in Maven with Groovy:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<goals>
<goal>set-system-properties</goal>
</goals>
<configuration>
<properties>
<property>
<name>micronaut.openapi.views.spec</name>
<value>rapidoc.enabled=true,openapi-explorer.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop</value>
</property>
</properties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
18.4 Swagger-UI
Views supports Swagger UI, to enable it use swagger-ui.enabled=true
.
The views will be generated to the META-INF/swagger/views/swagger-ui
directory of your project’s class output.
|
|
|
Override path to use to find the js file. Path must contain a files named swagger-ui.css, swagger-ui-bundle.js, swagger-ui-standalone-present.js. The value can be a URL path like: "https://unpkg.com/swagger-ui-dist/". Must contain trailing slash., |
|
Override path in swagger file path line. If you don’t set, then value will be |
|
Copy or not local JS and CSS resources. Set to |
|
|
|
Override path to use to find the theme CSS file., |
|
Copy or not local theme CSS resources. Set to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See Swagger UI Configuration for a description.
To expose the swagger-ui
views, you also must expose the generated yaml
:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut.router.static-resources.swagger-ui.paths=classpath:META-INF/swagger/views/swagger-ui
micronaut.router.static-resources.swagger-ui.mapping=/swagger-ui/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
[micronaut.router.static-resources.swagger-ui]
paths="classpath:META-INF/swagger/views/swagger-ui"
mapping="/swagger-ui/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
swaggerUi {
paths = "classpath:META-INF/swagger/views/swagger-ui"
mapping = "/swagger-ui/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
swagger-ui {
paths = "classpath:META-INF/swagger/views/swagger-ui"
mapping = "/swagger-ui/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
},
"swagger-ui": {
"paths": "classpath:META-INF/swagger/views/swagger-ui",
"mapping": "/swagger-ui/**"
}
}
}
}
}
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/swagger-ui.
See the guide for Visualize with Swagger UI an OpenAPI specification of your Micronaut application. to learn more. |
18.4.1 Swagger UI - OAuth 2.0 configuration
Swagger UI OAuth 2.0 integration allows you to obtain a token from an authorization server directly from Swagger UI. Then, when you use the Try This
button
in the Swagger UI, the requests issued incorporate a valid token.
You can configure it by setting the following properties:
|
|
|
|
|
|
|
|
|
|
When setting any of those properties, Micronaut will generate not only a swagger-ui/index.html
file, but also a
swagger-ui/oauth2-redirect.html
one. You will need to configure that endpoint as a callback URL in your OAuth 2 Authorization Server.
An example configuration could be:
micronaut.openapi.views.spec=swagger-ui.enabled=true,swagger-ui.theme=flattop,swagger-ui.oauth2RedirectUrl=http://localhost:8080/swagger-ui/oauth2-redirect.html,swagger-ui.oauth2.clientId=myClientId,swagger-ui.oauth2.scopes=openid,swagger-ui.oauth2.usePkceWithAuthorizationCodeGrant=true
Then, you configure the @SecurityScheme
:
@SecurityScheme(name = "openid",
type = SecuritySchemeType.OAUTH2,
scheme = "bearer",
bearerFormat = "jwt",
flows = @OAuthFlows(
authorizationCode = @OAuthFlow(
authorizationUrl = "https://mycompany.okta.com/oauth2/default/v1/authorize",
tokenUrl = "https://mycompany.okta.com/oauth2/default/v1/token",
refreshUrl = "",
scopes = @OAuthScope(name = "openid", description = "OpenID role")
)
)
)
@OpenAPIDefinition(
info = @Info(
title = "API service",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
And the appropriate @SecurityRequirement
on controllers e.g.:
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.rules.SecurityRule;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@Controller
@Secured(SecurityRule.IS_AUTHENTICATED)
public class OrderController {
@Get
@SecurityRequirement(name = "openid", scopes = "openid")
public String index() {
return "Example Response";
}
}
Do not forget to configure Micronaut Security accordingly:
micronaut.security.enabled=true
micronaut.security.token.jwt.enabled=true
micronaut.security.token.jwt.signatures.jwks.okta.url=https://mycompany.okta.com/oauth2/default/v1/keys
micronaut.security.intercept-url-map[0].pattern=/swagger-ui/**
micronaut.security.intercept-url-map[0].httpMethod=GET
micronaut.security.intercept-url-map[0].access[0]=isAnonymous()
micronaut.security.intercept-url-map[1].pattern=/swagger/**
micronaut.security.intercept-url-map[1].access[0]=isAnonymous()
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut.router.static-resources.swagger-ui.paths=classpath:META-INF/swagger/views/swagger-ui
micronaut.router.static-resources.swagger-ui.mapping=/swagger-ui/**
micronaut:
security:
enabled: true
token:
jwt:
enabled: true
signatures:
jwks:
okta:
url: 'https://mycompany.okta.com/oauth2/default/v1/keys'
intercept-url-map:
- pattern: /swagger-ui/**
httpMethod: GET
access:
- isAnonymous()
- pattern: /swagger/**
access:
- isAnonymous()
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
[micronaut]
[micronaut.security]
enabled=true
[micronaut.security.token]
[micronaut.security.token.jwt]
enabled=true
[micronaut.security.token.jwt.signatures]
[micronaut.security.token.jwt.signatures.jwks]
[micronaut.security.token.jwt.signatures.jwks.okta]
url="https://mycompany.okta.com/oauth2/default/v1/keys"
[[micronaut.security.intercept-url-map]]
pattern="/swagger-ui/**"
httpMethod="GET"
access=[
"isAnonymous()"
]
[[micronaut.security.intercept-url-map]]
pattern="/swagger/**"
access=[
"isAnonymous()"
]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
[micronaut.router.static-resources.swagger-ui]
paths="classpath:META-INF/swagger/views/swagger-ui"
mapping="/swagger-ui/**"
micronaut {
security {
enabled = true
token {
jwt {
enabled = true
signatures {
jwks {
okta {
url = "https://mycompany.okta.com/oauth2/default/v1/keys"
}
}
}
}
}
interceptUrlMap = [{
pattern = "/swagger-ui/**"
httpMethod = "GET"
access = ["isAnonymous()"]
}, {
pattern = "/swagger/**"
access = ["isAnonymous()"]
}]
}
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
swaggerUi {
paths = "classpath:META-INF/swagger/views/swagger-ui"
mapping = "/swagger-ui/**"
}
}
}
}
{
micronaut {
security {
enabled = true
token {
jwt {
enabled = true
signatures {
jwks {
okta {
url = "https://mycompany.okta.com/oauth2/default/v1/keys"
}
}
}
}
}
intercept-url-map = [{
pattern = "/swagger-ui/**"
httpMethod = "GET"
access = ["isAnonymous()"]
}, {
pattern = "/swagger/**"
access = ["isAnonymous()"]
}]
}
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
swagger-ui {
paths = "classpath:META-INF/swagger/views/swagger-ui"
mapping = "/swagger-ui/**"
}
}
}
}
}
{
"micronaut": {
"security": {
"enabled": true,
"token": {
"jwt": {
"enabled": true,
"signatures": {
"jwks": {
"okta": {
"url": "https://mycompany.okta.com/oauth2/default/v1/keys"
}
}
}
}
},
"intercept-url-map": [{
"pattern": "/swagger-ui/**",
"httpMethod": "GET",
"access": ["isAnonymous()"]
}, {
"pattern": "/swagger/**",
"access": ["isAnonymous()"]
}]
},
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
},
"swagger-ui": {
"paths": "classpath:META-INF/swagger/views/swagger-ui",
"mapping": "/swagger-ui/**"
}
}
}
}
}
The previous sample uses a remote Json Web Key Set to validate the token issued by the authorization server. If you use Micronaut Security OpenID Connect support with a server compatible with OpenID Connect Discovery, the JWKS of the authorization server is automatically configured.
18.5 Redoc
Views support Redoc, to enable it use redoc.enabled=true
.
The views will be generated to the META-INF/swagger/views/redoc
directory of your project’s class output.
|
|
|
Override path to use to find the js file. Path must contain a file named redoc.standalone.js. The value can be a URL path like: "https://unpkg.com/redoc/bundles/". Must contain trailing slash. |
|
Override path in swagger file path line. If you don’t set, then value will be |
|
Copy or not local JS and CSS resources. Set to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See Redoc config for a description of the above properties.
To expose the redoc
views, you also must expose the generated yaml
:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut.router.static-resources.redoc.paths=classpath:META-INF/swagger/views/redoc
micronaut.router.static-resources.redoc.mapping=/redoc/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
redoc:
paths: classpath:META-INF/swagger/views/redoc
mapping: /redoc/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
[micronaut.router.static-resources.redoc]
paths="classpath:META-INF/swagger/views/redoc"
mapping="/redoc/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
redoc {
paths = "classpath:META-INF/swagger/views/redoc"
mapping = "/redoc/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
redoc {
paths = "classpath:META-INF/swagger/views/redoc"
mapping = "/redoc/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
},
"redoc": {
"paths": "classpath:META-INF/swagger/views/redoc",
"mapping": "/redoc/**"
}
}
}
}
}
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/redoc.
18.6 OpenAPI Explorer
Views support OpenAPI Explorer, to enable it use openapi-explorer.enabled=true
.
The views will be generated to the META-INF/swagger/views/openapi-explorer
directory of your project’s class output.
|
|
|
Override path to use to find the js and other resource files. Path must contain a file named openapi-explorer.min.js. The value can be a URL path like: "https://unpkg.com/openapi-explorer/dist/browser/". Must contain trailing slash. |
|
Override path in swagger file path line. If you don’t set, then value will be |
|
Copy or not local JS and CSS resources. Set to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See OpenAPI Explorer Options for a description of the above properties.
To expose the openapi-explorer
views, you also must expose the generated yaml
:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut.router.static-resources.openapi-explorer.paths=classpath:META-INF/swagger/views/openapi-explorer
micronaut.router.static-resources.openapi-explorer.mapping=/openapi-explorer/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
openapi-explorer:
paths: classpath:META-INF/swagger/views/openapi-explorer
mapping: /openapi-explorer/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
[micronaut.router.static-resources.openapi-explorer]
paths="classpath:META-INF/swagger/views/openapi-explorer"
mapping="/openapi-explorer/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
openapiExplorer {
paths = "classpath:META-INF/swagger/views/openapi-explorer"
mapping = "/openapi-explorer/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
openapi-explorer {
paths = "classpath:META-INF/swagger/views/openapi-explorer"
mapping = "/openapi-explorer/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
},
"openapi-explorer": {
"paths": "classpath:META-INF/swagger/views/openapi-explorer",
"mapping": "/openapi-explorer/**"
}
}
}
}
}
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/openapi-explorer.
18.7 RapiDoc
The views will be generated to the META-INF/swagger/views/rapidoc
directory of your project’s class output.
|
|
|
Override path to use to find the js file. Path must contain a file named rapidoc-min.js. The value can be a URL path like: "https://unpkg.com/rapidoc/dist/". Must contain trailing slash. |
|
Override path in swagger file path line. If you don’t set, then value will be |
|
Copy or not local JS and CSS resources. Set to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See RapiDoc Options for a description.
To expose the rapidoc
views, you also must expose the generated yaml
:
micronaut.router.static-resources.swagger.paths=classpath:META-INF/swagger
micronaut.router.static-resources.swagger.mapping=/swagger/**
micronaut.router.static-resources.rapidoc.paths=classpath:META-INF/swagger/views/rapidoc
micronaut.router.static-resources.rapidoc.mapping=/rapidoc/**
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
rapidoc:
paths: classpath:META-INF/swagger/views/rapidoc
mapping: /rapidoc/**
[micronaut]
[micronaut.router]
[micronaut.router.static-resources]
[micronaut.router.static-resources.swagger]
paths="classpath:META-INF/swagger"
mapping="/swagger/**"
[micronaut.router.static-resources.rapidoc]
paths="classpath:META-INF/swagger/views/rapidoc"
mapping="/rapidoc/**"
micronaut {
router {
staticResources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
rapidoc {
paths = "classpath:META-INF/swagger/views/rapidoc"
mapping = "/rapidoc/**"
}
}
}
}
{
micronaut {
router {
static-resources {
swagger {
paths = "classpath:META-INF/swagger"
mapping = "/swagger/**"
}
rapidoc {
paths = "classpath:META-INF/swagger/views/rapidoc"
mapping = "/rapidoc/**"
}
}
}
}
}
{
"micronaut": {
"router": {
"static-resources": {
"swagger": {
"paths": "classpath:META-INF/swagger",
"mapping": "/swagger/**"
},
"rapidoc": {
"paths": "classpath:META-INF/swagger/views/rapidoc",
"mapping": "/rapidoc/**"
}
}
}
}
}
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/rapidoc.
18.8 RapiPdf
Views also supports RapiPdf, to enable it use rapipdf.enabled=true
.
RapiPdf supports the following options:
|
|
|
Override path to use to find the js file. Path must contain a file named rapdipdf-min.js. The value can be a URL path like: "https://unpkg.com/rapipdf/dist/". Must contain trailing slash., |
|
Override path in swagger file path line. If you don’t set, then value will be |
|
Copy or not local JS and CSS resources. Set to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See RapiPdf Attributes for a description.
It will add a button to the view to generate a PDF from the spec file.
19 Conversion to Asciidoc
You also can generate file in Asciidoc format. Then you can convert Asciidoc to another format: Markdown
, HTML
, PDF
or DOCX
.
To enable generation file in adoc format just set property -Dmicronaut.openapi.adoc.enabled=true
and add micronaut-openapi-adoc dependency to classpath:
annotationProcessor("io.micronaut.openapi:micronaut-openapi-adoc")
Adoc generator based on Apache Freemarker templates, and you can override full template or just customize part of them.
micronaut.openapi.adoc.enabled |
System property that enables or disables open api asciidoc conversion. | Default: true |
---|---|---|
|
Template directory path. |
Default: |
|
Main template filename. |
Default: |
|
Output directory path. |
Default: standard micronaut openapi output directory path |
|
Final |
Default: the same as openapi spec. filename |
|
Prefix for custom template names. You can override any of the templates that are used in the default implementation to change only part of the resulting document. |
See the guide for Generate API Documentation in AsciiDoc with the generated OpenAPI specification of your Micronaut application. to learn more. |
20 Versions and groups
Micronaut OpenAPI allows you to split the description of endpoints into several
files. To do this, you can use the versioning mechanism built into Micronaut
(using the @Version
annotation) and / or group endpoints using the @OpenAPIGroup
annotation or configuration.
20.1 Micronaut versioning
How to use the @Version
annotation can be read here: Micronaut API versioning.
Micronaut OpenAPI support this versioning and automatically add parameters or headers
with version from your application.yml
.
For example:
@Controller("/versioned")
public class VersionedController {
@Version("1")
@Get("/hello")
public String helloV1() {
return "helloV1";
}
@Version("2")
@Post("/hello")
public String helloV2(UserDto userDto) {
return "helloV2";
}
@Post("/common")
public String common() {
return null;
}
public static class UserDto {
public String name;
public int age;
public String secondName;
@NotNull
public String address;
}
}
micronaut.router.versioning.enabled=true
micronaut.router.versioning.parameter.enabled=true
micronaut.router.versioning.parameter.names=version
micronaut:
router:
versioning:
enabled: true
parameter:
enabled: true
names: version
[micronaut]
[micronaut.router]
[micronaut.router.versioning]
enabled=true
[micronaut.router.versioning.parameter]
enabled=true
names="version"
micronaut {
router {
versioning {
enabled = true
parameter {
enabled = true
names = "version"
}
}
}
}
{
micronaut {
router {
versioning {
enabled = true
parameter {
enabled = true
names = "version"
}
}
}
}
}
{
"micronaut": {
"router": {
"versioning": {
"enabled": true,
"parameter": {
"enabled": true,
"names": "version"
}
}
}
}
}
After processing the annotation processor, you will see 2 swagger files:
openapi: 3.0.1
info:
title: Api
version: "1.0"
paths:
/versioned/common:
post:
operationId: common
parameters:
- name: version
in: query
description: API version
schema:
type: string
responses:
"200":
description: common 200 response
content:
application/json:
schema:
type: string
/versioned/hello:
get:
operationId: helloV1
parameters:
- name: version
in: query
description: API version
schema:
type: string
responses:
"200":
description: helloV1 200 response
content:
application/json:
schema:
type: string
openapi: 3.0.1
info:
title: Api
version: "2.0"
paths:
/versioned/common:
post:
operationId: common
parameters:
- name: version
in: query
description: API version
schema:
type: string
responses:
"200":
description: common 200 response
content:
application/json:
schema:
type: string
/versioned/hello:
post:
operationId: helloV2
parameters:
- name: version
in: query
description: API version
schema:
type: string
requestBody:
content:
application/json:
schema:
type: object
properties:
userDto:
$ref: '#/components/schemas/UserDto'
required: true
responses:
"200":
description: helloV2 200 response
content:
application/json:
schema:
type: string
components:
schemas:
UserDto:
required:
- address
type: object
properties:
name:
type: string
age:
type: integer
format: int32
secondName:
type: string
address:
type: string
20.2 OpenAPI groups
To group endpoints according to different characteristics (API version,
entity type, public / private, etc.), you can use the @OpenAPIGroup
annotation.
With this annotation, you can specify which groups this endpoint belongs to and which groups it should be excluded from. An annotation can be written at the method, class or package level. You can also set binding to a group through the configuration, if you do not want to add annotations in the code.
You can set some additional settings for the group through system properties / application.yml / openapi.properties:
Usage example:
@Controller
public class ApiController {
@OpenAPIGroup("v2")
@Post("/save/{id}")
public String save2(String id, Object body) {
return "OK!";
}
@OpenAPIGroup({"v1", "v2"})
@Post("/save")
public String save(Object body) {
return "OK!";
}
}
micronaut.openapi.groups.v1.display-name=My API v1
micronaut.openapi.groups.v1.primary=true
micronaut.openapi.groups.v1.filename=myapiv1
micronaut.openapi.groups.v1.common-exclude=true
micronaut.openapi.groups.v1.packages=com.micronaut.controller.v1.*
micronaut.openapi.groups.v1.packages-exclude[0]=com.micronaut.controller.v1
micronaut.openapi.groups.v1.packages-exclude[1]=com.micronaut.controller
micronaut.openapi.groups.v2.display-name=My API v2
micronaut.openapi.groups.v3.testv3group.packages-exclude[0]=com.micronaut.controller.v3
micronaut.openapi.groups.v3.testv3group.packages-exclude[1]=com.micronaut.controller
micronaut:
openapi:
groups:
v1:
#The title that will be displayed in the group selector in the UI
display-name: My API v1
# A flag indicating that the swagger UI should select this group in the selector by default.
primary: true
# The name of the final swagger file. If it is not set, then by default the name will be generated according
# to the following pattern: swagger-<version>-<groupName>-<apiVersion>. version - application version from the main
# OpenAPIDefinition annotation, api-version - version from micronaut @Version annotation
filename: myapiv1
# Whether to exclude common endpoints from this group
common-exclude: true
# An additional way to add or remove endpoints from a given batch group.
# Strong package matching and inclusion of all subpackages is supported (* symbol)
packages: com.micronaut.controller.v1.*
packages-exclude:
- com.micronaut.controller.v1
- com.micronaut.controller
v2:
display-name: My API v2
"v3.testv3group":
packages-exclude:
- com.micronaut.controller.v3
- com.micronaut.controller
[micronaut]
[micronaut.openapi]
[micronaut.openapi.groups]
[micronaut.openapi.groups.v1]
display-name="My API v1"
primary=true
filename="myapiv1"
common-exclude=true
packages="com.micronaut.controller.v1.*"
packages-exclude=[
"com.micronaut.controller.v1",
"com.micronaut.controller"
]
[micronaut.openapi.groups.v2]
display-name="My API v2"
[micronaut.openapi.groups."v3.testv3group"]
packages-exclude=[
"com.micronaut.controller.v3",
"com.micronaut.controller"
]
micronaut {
openapi {
groups {
v1 {
displayName = "My API v1"
primary = true
filename = "myapiv1"
commonExclude = true
packages = "com.micronaut.controller.v1.*"
packagesExclude = ["com.micronaut.controller.v1", "com.micronaut.controller"]
}
v2 {
displayName = "My API v2"
}
v3.testv3group {
packagesExclude = ["com.micronaut.controller.v3", "com.micronaut.controller"]
}
}
}
}
{
micronaut {
openapi {
groups {
v1 {
display-name = "My API v1"
primary = true
filename = "myapiv1"
common-exclude = true
packages = "com.micronaut.controller.v1.*"
packages-exclude = ["com.micronaut.controller.v1", "com.micronaut.controller"]
}
v2 {
display-name = "My API v2"
}
"v3.testv3group" {
packages-exclude = ["com.micronaut.controller.v3", "com.micronaut.controller"]
}
}
}
}
}
{
"micronaut": {
"openapi": {
"groups": {
"v1": {
"display-name": "My API v1",
"primary": true,
"filename": "myapiv1",
"common-exclude": true,
"packages": "com.micronaut.controller.v1.*",
"packages-exclude": ["com.micronaut.controller.v1", "com.micronaut.controller"]
},
"v2": {
"display-name": "My API v2"
},
"v3.testv3group": {
"packages-exclude": ["com.micronaut.controller.v3", "com.micronaut.controller"]
}
}
}
}
}
After processing the annotation processor, you will get 3 different swagger files.
Integration with UI
In rapidoc and redoc do not support multiple files at the same time, so for normal API display, you will have to complete the template yourself.
Swagger UI has support for multiple files out of the box. The example above would look something like this:
21 Server Context
In the micronaut configuration file you can define a server context path (with micronaut.server.context-path
) which serves as a base path for all routes.
Since the yaml specification file and the views are generated at compile time, these resources are not aware of changes during runtime (e.g. context-path is determined by a reverse proxy).
It is still possible for the views to work in case a context path is defined:
-
Set
micronaut.openapi.server.context.path
property for compile time resolution, or -
Use a
HttpServerFilter
that will add a cookie, or -
Add a parameter to the url.
The view will first look for the cookie and if not present for the parameter.
21.1 Compile Time Resolution
Either set micronaut.openapi.server.context.path
as a System Property or in openapi.properties
, then all paths will be prepended with the specified value at compile time.
If you want the resolution of the context path at runtime use one of the following methods:
21.2 HttpServerFilter
Use a HttpServerFilter
to add a cookie which contains the context-path. This can be done in two ways:
-
Set the context-path from a static property (has to be set during compile time), or
-
Parse the context path from the request headers. This is particularly useful if your application runs behind a reverse proxy, which strips the context-path before forwarding the request to the application. Most reverse proxies should provide the possibility to set the stripped context-path as a header (e.g. X-Forwarded-Prefix in the case of traefik).
Static Property
Create a HttpServerFilter
that will add a cookie with name contextPath
.
import java.time.Duration;
import org.reactivestreams.Publisher;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
@Requires(property = "micronaut.server.context-path")
@Filter(methods = {HttpMethod.GET, HttpMethod.HEAD}, patterns = {"/**/rapidoc*", "/**/redoc*", "/**/swagger-ui*", "/**/openapi-explorer*"})
public class OpenApiViewCookieContextPathFilter implements HttpServerFilter {
private final Cookie contextPathCookie;
OpenApiViewCookieContextPathFilter(@Value("${micronaut.server.context-path}") String contextPath) {
contextPathCookie = Cookie.of("contextPath", contextPath).maxAge(Duration.ofMinutes(2L));
}
@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return Publishers.map(chain.proceed(request), response -> response.cookie(contextPathCookie));
}
}
From HTTP Header
The HttpServerFilter
looks very similar to the one above. The main difference is that it parses the context-path
value from the request headers.
import java.time.Duration;
import org.reactivestreams.Publisher;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
@Filter(
methods = {HttpMethod.GET, HttpMethod.HEAD},
patterns = {"/**/rapidoc*", "/**/redoc*", "/**/swagger-ui*", "/**/openapi-explorer*"}
)
@Requires(property = "micronaut.server.context-path-header")
public class OpenApiContextPathFilter implements HttpServerFilter {
private final String contextPathHeader;
OpenApiContextPathFilter(@Value("${micronaut.server.context-path-header}") String contextPathHeader) {
this.contextPathHeader = contextPathHeader;
}
@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
final String contextPath = request.getHeaders().get(contextPathHeader);
if (contextPath != null) {
Cookie contextPathCookie = Cookie.of("contextPath", contextPath).maxAge(Duration.ofMinutes(2L));
return Publishers.map(chain.proceed(request), response -> response.cookie(contextPathCookie));
} else {
return chain.proceed(request);
}
}
}
21.3 URL Parameter
Just add a parameter to the view url. For instance if the context path is set to /context/path
you will access your view with http://localhost:8080/context/path/swagger-ui?contextPath=%2Fcontext%2Fpath
.
22 Micronaut OpenAPI with Spring
You can use micronaut-openapi
to build openapi specification for Spring
/ Spring Boot
applications. In this case, you do not need to change anything in the code. You can continue to use spring, and use micronaut as a replacement for such libraries as springdoc-openapi
, springfox
, swagger
etc.
22.1 Spring with Gradle
To use micronaut-openapi with spring in gradle add this code to you build.gradle
:
dependencies {
// add to annotationProcessor and compileOnly blocks next libraries:
annotationProcessor("io.micronaut:micronaut-inject-java:$micronautCoreVersion")
annotationProcessor(platfom("io.micronaut.spring:micronaut-spring-bom:$micronautSpringVersion"))
annotationProcessor("io.micronaut.spring:micronaut-spring-annotation")
annotationProcessor("io.micronaut.spring:micronaut-spring-web-annotation")
annotationProcessor("io.micronaut.spring:micronaut-spring-boot-annotation")
annotationProcessor("io.micronaut.openapi:micronaut-openapi:$micronautOpenapiVersion")
compileOnly("io.micronaut:micronaut-inject-java:$micronautCoreVersion")
compileOnly("io.micronaut.openapi:micronaut-openapi-annotations:$micronautOpenapiVersion")
compileOnly("io.micronaut.serde:micronaut-serde-api:$micronautSerdeVersion")
}
For kotlin just change block annotationProcessor
to kapt
or ksp
.
22.2 Spring with Maven
To use micronaut-openapi with spring in maven add this code to your pom.xml
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.core.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi-annotations</artifactId>
<version>${micronaut.openapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micronaut.serde</groupId>
<artifactId>micronaut-serde-api</artifactId>
<version>${micronaut.serde.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.core.version}</version>
</path>
<path>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</path>
<path>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-web-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</path>
<path>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-boot-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</path>
<path>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi</artifactId>
<version>${micronaut.openapi.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
For kotlin .pom.xml
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.core.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi-annotations</artifactId>
<version>${micronaut.openapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.micronaut.serde</groupId>
<artifactId>micronaut-serde-api</artifactId>
<version>${micronaut.serde.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
<annotationProcessorPaths combine.self="override">
<annotationProcessorPath>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.core.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-web-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.micronaut.spring</groupId>
<artifactId>micronaut-spring-boot-annotation</artifactId>
<version>${micronaut.spring.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi</artifactId>
<version>${micronaut.openapi.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
23 Guides
See the following list of guides to learn more about working with OpenAPI in the Micronaut Framework:
24 Repository
You can find the source code of this project in this repository:
25 Breaking Changes
This section documents breaking changes between Micronaut OpenAPI versions:
Micronaut OpenAPI 4.0.0
Micronaut OpenAPI no longer generates 200
or default
HTTP status code responses when using @ApiResponse
annotation. It’s up to the user to define all the appropriate status codes.