There are a lot of how-tos on how to use OpenAPI to document the REST APIs of a Quarkus microservice. However, none of them show you how to document the schema of your API response.

In this blog post I’ll show you two ways to add schema documentation to your OpenAPI spec.

First, let’s create a simple REST resource:

Listing 1. BlogResource.kt
@Path("/blog")
class BlogResource {
    private val posts = mutableListOf(EXAMPLE_POST)

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    fun list(): List<Post> {
        return posts
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    fun create(post: Post): Response {
        posts += post
        return Response.status(Status.CREATED).build()
    }

    companion object {
        private val EXAMPLE_POST = TODO("omitted")
    }
}

We can validate the generated basic OpenAPI spec by using the built-in SwaggerUI. To validate we run the example application using gradle quarkusDev. Then browse to http://localhost:8080/q/swagger-ui/ and you’ll be greeted with a SwaggerUI that looks like this:

Swagger UI Example

It shows us which endpoints are available and which fields are in the response, but it doesn’t say what those fields mean. So, let’s add some documentation to the response fields!

First, we need to add the @APIResponseSchema to the endpoint:

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @APIResponseSchema(Post::class)             // <-- add this annotation
    fun list(): List<Post> {
        return posts
    }

Next, we can add the documentation to the response class in two different ways. The first is to add the @Schema annotation to both the class and the fields:

Listing 2. Post.kt
@Schema(name = "Blog post response")
data class Post(
    @field:Schema(implementation = Author::class)
    val author: Author,

    @field:Schema(
        title = "One or more tags which describe the subject of the post.",
        minItems = 1,
    )
    val tags: List<String>,

    @field:Schema(
        title = "The publication date of this post.",
        format = "ISO8601 date string",
        example = "2022-10-14",
    )
    val publishedOn: LocalDate,

    @field:Schema(title = "The title of this blog post.")
    val title: String,

    @field:Schema(
        title = "The content of this blog post",
        format = "HTML"
    )
    val post: String,

    @field:Schema(implementation = Comment::class)
    val comments: List<Comment>
)

Note that Quarkus requires the annotation to be set on the field, so we have to add the field use-site target.

For the Author and Comment classes we’ll document the fields directly in the @Schema class annotation:

Listing 3. Author.kt
@Schema(
    name = "Blog post author",
    properties = [
        SchemaProperty(
            name = "fullName",
            title = "Full name of the author."
        ),
        SchemaProperty(
            name = "photo",
            title = "A picture of the author.",
            format = "URL",
            example = "https://example.com/picture.jpg",
        ),
    ]
)
data class Author(
    val fullName: String,
    val photo: String,
)

The @SchemaProperty annotation is very similar to the @Schema annotation we used in the example above. Since the annotation is not placed on the field we need to provide the name of the field.

This second way of documenting the class keeps the documentation (a bit) more separated from the code, but is a bit more error-prone when you rename a field, for example. Personally I like to place the documentation directly on the fields. It makes it clear which piece of documentation belongs to which field and it makes refactoring less error-prone.

Now that we’ve added some documentation to the response, we can restart our application and verify that the response schema is now documented:

Swagger UI Example with response documentation

A working example can be found on my Github. Tested with Quarkus 2.13.2 and Kotlin 1.7.20.

shadow-left