Integrating Swagger and OpenAPI in Spring Boot: A Step-by-Step Guide
This chapter provides a comprehensive, step-by-step guide to integrating and customizing Swagger and OpenAPI in a Spring Boot application. You will learn how to transition from undocumented APIs to a fully interactive, secure, and production-ready API documentation portal, including architectural refactoring using Data Transfer Objects (DTOs) and global security configurations.
1. The Problem: Why Do We Need API Documentation?
When building RESTful web services, backend developers rarely work in isolation. The APIs you build must be consumed by other stakeholders, such as:
- Frontend Developers: Building web or mobile user interfaces.
- Third-Party Integrators: Consuming your services for external partnerships.
- QA Engineers: Testing API contracts and edge cases.
Without formal documentation, these consumers face significant friction:
- They do not know what endpoints are available.
- They are unaware of the required HTTP verbs (
GET,POST,PUT,DELETE). - They do not know what path parameters, query parameters, headers, or request bodies are expected.
- They have no interactive way to test the APIs without setting up external tools like Postman.
Real-Life Analogy
Imagine walking into a restaurant that has no menu. To order food, you must guess what the chef can cook, guess the price, and guess how to ask for it. API documentation is the menu of your backend application. It lists exactly what is available, what ingredients (parameters) are required, and what you will get in return.
2. Core Mental Models: OpenAPI Specification (OAS) vs. Swagger
Before writing code, it is crucial to understand the distinction between OpenAPI Specification and Swagger, as these terms are often used interchangeably but refer to different things.
OpenAPI Specification (OAS)
OpenAPI is a community-driven, open-source specification (a standard format) for describing RESTful APIs. It defines a standardized way to describe API structures, including HTTP methods, request/response formats, headers, and authentication schemes. It is language-agnostic and readable by both humans and machines.
Swagger
Swagger is a suite of tools built by SmartBear to implement and work with the OpenAPI Specification.
- Swagger UI: Renders an interactive HTML page based on an OpenAPI definition, allowing users to visualize and test APIs directly in the browser.
- Swagger Editor: A browser-based editor where you can write and edit OpenAPI specifications.
3. Choosing the Right Library: SpringFox vs. SpringDoc OpenAPI
To integrate Swagger into a Spring Boot application, Java developers historically chose between two primary libraries: SpringFox and SpringDoc OpenAPI.
Because SpringDoc OpenAPI is actively maintained, fully supports Spring Boot 3.x, and offers superior performance, it is the library of choice for modern enterprise applications.
4. Step-by-Step Integration of SpringDoc OpenAPI
To integrate Swagger UI into your Spring Boot application, you only need to add a single dependency to your build configuration.
Step 1: Add the Dependency
Add the following dependency to your pom.xml file:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
Note: If you are using an older Spring Boot 2.x application, the dependency artifact ID is
springdoc-openapi-ui. For Spring Boot 3.x, always use thespringdoc-openapi-starter-webmvc-uiartifact.
Step 2: Run the Application
Once the dependency is added and Maven imports are reloaded, start your Spring Boot application. By default, SpringDoc automatically scans all classes annotated with @RestController and generates the API documentation.
5. Customizing Swagger UI Paths and Accessing Endpoints
By default, SpringDoc exposes the interactive UI and the raw JSON documentation at specific endpoints.
Default Access URLs
- Interactive Swagger UI:
http://localhost:8080/swagger-ui/index.html - Raw OpenAPI JSON:
http://localhost:8080/v3/api-docs
Customizing the UI Path
If you want to expose the documentation under a shorter or custom URL (such as /docs), you can configure this in your application properties file.
In application.yml:
springdoc:
swagger-ui:
path: /docs
In application.properties:
springdoc.swagger-ui.path=/docs
Now, navigating to http://localhost:8080/docs will automatically redirect you to the interactive Swagger UI page.
How Swagger UI Works Under the Hood
6. Customizing API Metadata via Java Configuration
Out of the box, Swagger UI displays generic titles and version numbers. To make your API documentation professional, you can customize the metadata (Title, Description, Version, and Server Environments) by defining an OpenAPI bean in a configuration class.
Create a new configuration class named SwaggerConfig:
package com.example.journalapp.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI myCustomOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Journal Application API")
.description("Backend APIs for managing journal entries and user profiles.")
.version("1.0.0"))
.servers(List.of(
new Server().url("http://localhost:8081").description("Local Development Server"),
new Server().url("https://api.journalapp.com").description("Production Server")
));
}
}
Key Components Configured:
Info: Sets the title, description, and version of your API.Server: Defines the environment URLs. Users can switch between these servers in the Swagger UI dropdown to execute API requests against different environments (e.g., Local vs. Staging/Production).
7. Organizing and Grouping APIs with @Tag and @Operation
By default, Swagger groups endpoints based on the controller class names. You can customize these group names, add descriptions, and document individual endpoints using annotations.
Step 1: Grouping Controllers with @Tag
Use the @Tag annotation at the class level to rename and describe a controller group.
package com.example.journalapp.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
@Tag(name = "User APIs", description = "Operations related to user registration, updates, and profile deletion")
public class UserController {
// Endpoints go here
}
Step 2: Documenting Endpoints with @Operation
Use the @Operation annotation at the method level to provide a brief summary of what an individual API endpoint does.
package com.example.journalapp.controller;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HealthCheckController {
@GetMapping("/health-check")
@Operation(summary = "Perform a system health check", description = "Returns HTTP 200 OK if the service is up and running.")
public ResponseEntity<String> healthCheck() {
return ResponseEntity.ok("OK");
}
}
8. Architectural Refactoring: Entities vs. DTOs in API Documentation
A common anti-pattern in Spring Boot development is exposing database entity classes directly in controller request bodies. This practice degrades API documentation and exposes internal database schemas.
The Problem with Exposing Entities
If your User entity contains database-specific fields (such as MongoDB's ObjectId, internal indexing configurations, or sensitive fields like hashed passwords), Swagger UI will display all of these fields in the request body schema.
The Solution: Data Transfer Objects (DTOs)
By introducing a clean UserDTO class, you expose only the fields that the client actually needs to send during registration or updates.
Code Evolution: Refactoring to DTOs
1. The Naive Approach (Exposing the Entity)
// BAD: Exposing DB Entity directly in the controller
@PostMapping("/signup")
public ResponseEntity<?> signUp(@RequestBody User user) {
userService.saveNewUser(user);
return ResponseEntity.ok(user);
}
2. The Clean Production-Ready Approach (Using DTOs)
Create a lightweight UserDTO class and annotate it with @Schema to customize how fields appear in Swagger UI:
package com.example.journalapp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserDTO {
@NotBlank
@Schema(description = "The unique username of the user", example = "john_doe")
private String username;
@NotBlank
@Schema(description = "The plain-text password", example = "SecureP@ss123")
private String password;
@Schema(description = "User's primary email address", example = "john.doe@example.com")
private String email;
@Schema(description = "Flag to enable sentiment analysis on journal entries", example = "true")
private boolean sentimentAnalysis;
}
3. Update the Controller to Accept the DTO
@PostMapping("/signup")
@Operation(summary = "Register a new user", description = "Creates a new user profile with the provided credentials.")
public ResponseEntity<?> signUp(@RequestBody UserDTO userDTO) {
// Map DTO to Entity
User user = new User();
user.setUsername(userDTO.getUsername());
user.setPassword(userDTO.getPassword());
user.setEmail(userDTO.getEmail());
user.setSentimentAnalysis(userDTO.isSentimentAnalysis());
userService.saveNewUser(user);
return ResponseEntity.ok("User registered successfully");
}
Benefits of this Refactoring:
- Security: Internal fields like database IDs or salt values are hidden from the client.
- Validation: Validation annotations (like
@NotBlank) are clearly marked with a red asterisk in Swagger UI, indicating they are mandatory fields. - Clarity: The
@Schemaannotation provides clear descriptions and realistic placeholder values for API consumers.
9. Handling Global Security (JWT/Token Authentication) in Swagger UI
If your API endpoints are secured using Spring Security (e.g., requiring a JWT token in the Authorization header), Swagger UI will fail to execute requests out of the box, returning 401 Unauthorized or 403 Forbidden errors.
To fix this, you must configure Swagger to support global security schemes. This adds an interactive "Authorize" lock button to the Swagger UI, allowing users to input their token once and automatically attach it to all subsequent API requests.
Updating SwaggerConfig with Security Schemes
Update your SwaggerConfig class to define a Bearer Token security scheme:
package com.example.journalapp.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI myCustomOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Journal Application API")
.description("Backend APIs for managing journal entries and user profiles.")
.version("1.0.0"))
.servers(List.of(
new Server().url("http://localhost:8081").description("Local Development Server")
))
.addSecurityItem(new SecurityRequirement().addList("BearerToken"))
.components(new Components()
.addSecuritySchemes("BearerToken", new SecurityScheme()
.name("BearerToken")
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name("Authorization")
));
}
}
How to Use the Security Feature in Swagger UI:
- Execute your
/loginendpoint in Swagger UI to obtain a JWT token. - Click the Authorize button (represented by a padlock icon) at the top of the Swagger UI page.
- Paste your JWT token into the input field and click Authorize.
- All subsequent requests made via Swagger UI will automatically include the
Authorization: Bearer <your_token>header.
10. Resolving Complex Types: Refactoring Path Parameters
When using database-specific types like MongoDB's ObjectId directly in controller path parameters, Swagger UI fails to render a simple text input field. Instead, it serializes the complex internal structure of ObjectId (containing timestamps, machine identifiers, and dates) as a nested JSON object.
The Problem
// BAD: Swagger UI will render a complex nested object for ObjectId
@GetMapping("/id/{myId}")
public ResponseEntity<JournalEntry> getJournalEntryById(@PathVariable ObjectId myId) {
JournalEntry entry = journalEntryService.findById(myId);
return ResponseEntity.ok(entry);
}
The Solution
Change the controller method signature to accept a standard String representation of the ID. Convert the String to an ObjectId inside the controller or service layer.
// GOOD: Swagger UI renders a clean, simple text input field
@GetMapping("/id/{myId}")
@Operation(summary = "Get a journal entry by ID")
public ResponseEntity<JournalEntry> getJournalEntryById(@PathVariable String myId) {
// Convert String to ObjectId internally
org.bson.types.ObjectId objectId = new org.bson.types.ObjectId(myId);
JournalEntry entry = journalEntryService.findById(objectId);
return ResponseEntity.ok(entry);
}
This simple refactoring ensures that Swagger UI displays a clean, user-friendly text box for the path parameter, allowing developers to paste standard 24-character hexadecimal string IDs directly.
11. Summary and Key Takeaways
- API Documentation is Essential: It serves as an interactive contract between backend developers and API consumers (frontend, QA, third parties).
- OpenAPI vs. Swagger: OpenAPI is the standardized specification format; Swagger is the suite of tools (like Swagger UI) used to visualize and interact with that specification.
- SpringDoc OpenAPI: This is the modern, actively maintained library for integrating OpenAPI 3 into Spring Boot applications, replacing the legacy SpringFox library.
- Automatic Scanning: SpringDoc automatically scans
@RestControllerclasses and generates documentation at/v3/api-docsand/swagger-ui/index.html. - Custom Metadata: You can customize titles, descriptions, and server environments by defining an
OpenAPIbean in a@Configurationclass. - Use DTOs, Not Entities: Exposing raw database entities in controllers pollutes your API documentation. Use DTOs combined with
@Schemaannotations to keep your API contracts clean and secure. - Global Security Configuration: By defining a
SecuritySchemeandSecurityRequirementin yourOpenAPIbean, you can enable global JWT/Bearer token authorization directly inside Swagger UI. - Keep Path Parameters Simple: Avoid exposing complex types like
ObjectIdin controller signatures. Use standardStringparameters and convert them internally to maintain a clean user interface.