1. Introduction
In this tutorial, we’ll generate Swagger UI for REST endpoints using a YML file and schema reference. We’ll keep all the code needed to generate APIs separate, following the Swagger parsing rules.
We’ll also understand the implementation from scratch for generating Swagger UI using YML file.
2. Having Swagger Logic in YML File
Let’s check the case of putting Swagger logic in a separate Swagger (YML or JSON) file:
openapi: 3.0.1
info:
title: User API
version: "1.0"
description: API for User Management
paths:
/users/all:
get:
summary: Get all users
description: Returns a list of users
responses:
"200":
description: Successful response
A key benefit here is the clear separation of concerns, which keeps documentation details from cluttering the actual application code. Moreover, this approach often facilitates easier collaboration, allowing non-developers like QA testers or Product Managers to contribute to or update the API documentation directly.
Another significant plus is the reusability – the same documentation file can frequently be shared or adapted across multiple microservices or teams.
Furthermore, maintaining the documentation separately enables dedicated version control and supports more formal API governance procedures by tracking changes independently.
However, this method certainly comes with its own challenges. Perhaps the most notable drawback is the necessity for manual updates; if the API code changes, the separate documentation file must be updated.
Finally, debugging discrepancies between the actual API behavior and the documented specification can sometimes be more difficult because there isn’t a direct link between the API methods in the code and their descriptions in the separate file.
3. Getting Started
Let’s get started with setting up a Spring Boot project. To do this, we can navigate to Spring Initializer and apply the given configurations:
- Project: Maven
- Language: Java
- Spring Boot Version: 3.4.4 (or any stable release)
- Group: com.baeldung
- Artifact: swaggeryml
- Description: Demo project for Swagger using YML
- Packaging: JAR
- JDK: 17
We’d also need a spring-doc dependency, which can make this possible:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.4.0</version>
</dependency>
Along with that, we’ll also need spring-boot-starter-web dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
The spring-boot-starter-web is the key dependency that brings in Spring MVC itself and, importantly, an embedded web server (like Tomcat by default). It sets up all the necessary infrastructure for our Spring Boot application to function as a web application, capable of handling incoming HTTP requests on specific paths (/swagger-ui.html, /v3/api-docs, and our own defined endpoints).
4. Adding application.properties
We have to do some additional configuration in applicaiton.properties so that our YML file is detected and picked:
spring.application.name=demo
springdoc.api-docs.enabled=true
springdoc.api-docs.path=/v3/api-docs
springdoc.api-docs.resolve-schema-properties=true
springdoc.swagger-ui.url=/openapi.yml
springdoc.swagger-ui.path=/swagger-ui.html
First off, spring.application.name=demo is our application name. Moving on to the documentation specifics, springdoc.api-docs.enabled=true is essential as it enables the springdoc-openapi library to actively scan our code for things like controllers and @Operation annotations and dynamically generate an OpenAPI v3 specification.
Furthermore, springdoc.api-docs.path=/v3/api-docs clearly defines the specific endpoint where this dynamically generated specification will be made available. Similarly, springdoc.swagger-ui.path=/swagger-ui.html correctly sets the URL where we’ll conveniently access the interactive Swagger UI interface in our browser, perhaps at http://localhost:8080/swagger-ui.html.
However, for our specific objective, springdoc.swagger-ui.url=/openapi.yml is the truly crucial part. It instructs the Swagger UI to fetch and display the definition found at our designated /openapi.yml path.
Finally, springdoc.api-docs.resolve-schema-properties=true ensures that springdoc-openapi not only generates $ref links but also properly includes the corresponding component definitions, like components/schemas/Student.
5. Understanding Swagger YML File
Now, let’s take a look at OpenAPI definitions in a YML file, which will be residing in the resources/static/components/schemas folder.
5.1. Adding Versioning Related Info
Let’s add the version of the OpenAPI Specification that the document adheres to:
openapi: 3.1.0
info:
title: Student API
description: Following documentation explain the API's supported by the [student server](http://localhost:8080).
version: 1.1.9
An OpenAPI YML file begins with the openapi keyword, which specifies the version of the OpenAPI Specification being used. Following this, the info section provides a concise overview of the API documentation.
5.2. Adding Server-Related Info
Next, we have the servers section:
servers:
- url: http://localhost:8080/v1
description: Prod server
variables:
region:
default: us-west
enum:
- us-west
- us-east
- url: http://localhost:8080/test
description: Test server
The servers section is where we define information about the various server environments.
5.3. Adding REST Endpoints in YML
Now we’re ready to define our REST endpoints:
paths:
/students:
get:
tags:
- Students
summary: Returns all the students.
description: Following path gives all the data related to students
responses:
"200":
description: A JSON array of student objects
content:
application/json:
schema:
type: array
items:
$ref: './components/schemas/Student.yml'
/students/{id}:
get:
tags:
- Students
summary: Gets a student by ID.
description: >
Gives the details of specific student based on **ID**
operationId: getStudentById
parameters:
- name: id
in: path
description: Student ID
required: true
schema:
type: integer
format: int64
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: './components/schemas/Student.yml'
From there, the crucial paths section holds the complete definitions for all the individual API endpoints our application exposes. For instance, a specific path like /students details operations like GET, which specifies an HTTP method available for that path. Within these operations, tags are used to group related operations for better organization. A summary offers a brief, human-readable overview of the operation’s purpose.
Crucially, the responses section outlines all possible responses for that HTTP operation, typically keyed by their HTTP status code. To elaborate on a specific response, the description provides details about its content, and the content object defines the actual payload format, such as application/json.
Within content, the schema keyword introduces the structure of the response body, and if it’s an array, items then describe the schema of each individual element. Quite often, we’ll see $ref here, indicating that the schema isn’t defined inline but rather referenced from another location, like ./components/schemas/Student.yml, which points to that external definition.
We need to keep in mind that all the keywords used in the YML file are case-sensitive.
5.4. Adding Schema Definition Files
It’s often a good practice to use references for schema definition and organize them in OpenAPI (Swagger) to describe the structure of a data object.
Here’s the schema definition that we’ve used, and it should be placed in the resources/static/components/schemas folder:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
rollNo:
type: string
5.5. Output
When we run the application, we can see it’s documenting a “Student API“, version 1.1.9, built with OpenAPI Specification 3.1. It clearly lays out details like the production server’s URL (http://localhost:8080/v1) and even allows us to see specific endpoints like the /students GET request, which is designed to retrieve all student data. We can see the output by navigating to the Swagger URL:
6. Conclusion
In this article, we explored the ins and outs of generating Swagger UI by maintaining it separately in a separate YML file. Choosing the right approach really boils down to our project’s specific context. For smaller projects or rapid prototyping, the annotation-based method offers speed and tight synchronization, making maintenance straightforward.
However, for larger, more complex systems like microservices, or when collaboration and formal API governance are key, separating our documentation into a YML file provides better structure, reusability, and a cleaner codebase.
As always, the code for this article is available over on GitHub.