SpringDoc annotation reference
This reference provides comprehensive documentation of SpringDoc annotations used to generate OpenAPI documentation from Java code. Use this guide to understand how annotations create documentation and collaborate effectively with developers.
Overview: SpringDoc annotations
SpringDoc uses annotations to automatically generate OpenAPI 3.0 specifications from Java code. These annotations are added directly to controller classes, methods, and parameters to create comprehensive API documentation.
Annotation categories
| Category | Purpose | Key Annotations |
|---|---|---|
| Endpoint Documentation | Document API operations | @Operation, @ApiResponses |
| Parameter Documentation | Document request parameters | @Parameter, @RequestBody |
| Response Documentation | Document API responses | @ApiResponse, @Content, @Schema |
| Organization | Group and organize endpoints | @Tag, @Hidden |
| Examples | Provide request/response examples | @ExampleObject, @Example |
| Schema Definition | Define data models | @Schema, @ArraySchema |
Core endpoint annotations
@Operation
Purpose: Documents the main information for an API endpoint.
Location: Method level (on controller methods)
Example:
@Operation(
summary = "Create a new customer",
description = """
Creates a new customer with complete profile information including nested address data.
This endpoint demonstrates:
- Nested object handling (Address within Customer)
- Enum validation (CustomerType)
- Input validation and error handling
- Database persistence with JPA
""",
tags = {"Single customer CRUD operations"},
operationId = "createCustomer"
)
@PostMapping("/customer")
public String createCustomer(@RequestBody Customer customer) {
// Implementation
}
Generated OpenAPI:
paths:
/customer:
post:
summary: Create a new customer
description: |
Creates a new customer with complete profile information including nested address data.
This endpoint demonstrates:
- Nested object handling (Address within Customer)
- Enum validation (CustomerType)
- Input validation and error handling
- Database persistence with JPA
tags:
- Single customer CRUD operations
operationId: createCustomer
Key Properties:
summary- Brief description (appears in endpoint lists)description- Detailed explanation (supports markdown)tags- Groups endpoints in Swagger UIoperationId- Unique identifier for code generationdeprecated- Mark endpoint as deprecatedhidden- Hide endpoint from documentation
@ApiResponses and @ApiResponse
Purpose: Documents all possible HTTP responses for an endpoint.
Location: Method level (on controller methods)
Example:
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Customer created successfully",
content = @Content(
mediaType = "text/plain",
examples = @ExampleObject(value = "Customer added successfully")
)
),
@ApiResponse(
responseCode = "400",
description = "Invalid customer data provided",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(value = """
{
"message": "Invalid customer data: ID format must be CUSTXXX",
"timestamp": "2026-01-25T10:30:00.000Z",
"status": 400,
"error": "Bad Request"
}
""")
)
),
@ApiResponse(
responseCode = "409",
description = "Customer with this ID already exists"
)
})
Generated OpenAPI:
responses:
'200':
description: Customer created successfully
content:
text/plain:
examples:
default:
value: "Customer added successfully"
'400':
description: Invalid customer data provided
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
default:
value:
message: "Invalid customer data: ID format must be CUSTXXX"
timestamp: "2026-01-25T10:30:00.000Z"
status: 400
error: "Bad Request"
'409':
description: Customer with this ID already exists
Key Properties:
responseCode- HTTP status code (as string)description- Response descriptioncontent- Response body specificationheaders- Response headers documentation
Parameter documentation annotations
@Parameter
Purpose: Documents request parameters (path, query, header, cookie).
Location: Parameter level (on method parameters)
Path Parameter Example:
@GetMapping("/customer/{customerID}")
public Customer getCustomer(
@Parameter(
description = "Unique customer identifier in format CUSTXXX",
example = "CUST001",
required = true
)
@PathVariable String customerID
) {
// Implementation
}
Query Parameter Example:
@GetMapping("/customers")
public PaginatedResponse<Customer> getCustomers(
@Parameter(
description = "Page number for pagination (starts at 0)",
example = "0",
schema = @Schema(type = "integer", minimum = "0")
)
@RequestParam(defaultValue = "0") int page,
@Parameter(
description = "Number of items per page",
example = "10",
schema = @Schema(type = "integer", minimum = "1", maximum = "100")
)
@RequestParam(defaultValue = "10") int size
) {
// Implementation
}
Generated OpenAPI:
/customer/{customerID}:
get:
parameters:
- name: customerID
in: path
description: Unique customer identifier in format CUSTXXX
required: true
example: "CUST001"
schema:
type: string
/customers:
get:
parameters:
- name: page
in: query
description: Page number for pagination (starts at 0)
required: false
example: 0
schema:
type: integer
minimum: 0
- name: size
in: query
description: Number of items per page
required: false
example: 10
schema:
type: integer
minimum: 1
maximum: 100
Request body documentation
Purpose: Documents request body content and structure.
Example:
@PostMapping("/customer")
public String createCustomer(
@Parameter(
description = "Customer object with all required fields including nested address",
required = true,
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Customer.class),
examples = @ExampleObject(
name = "Complete Customer Example",
summary = "Customer with all fields",
description = "Example showing a complete customer record with all optional fields included",
value = """
{
"id": "CUST001",
"name": "Alice Johnson",
"address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zipCode": "10001",
"country": "USA"
},
"phone": "555-0123",
"type": "VIP"
}
"""
)
)
)
@RequestBody Customer customer
) {
// Implementation
}
Schema and model annotations
@Schema (on Classes)
Purpose: Documents data model structure and validation rules.
Location: Class level and field level
Class Level Example:
@Schema(
description = "Complete customer information including contact details and address",
example = """
{
"id": "CUST001",
"name": "Alice Johnson",
"phone": "555-0123",
"type": "VIP",
"address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zipCode": "10001",
"country": "USA"
}
}
"""
)
@Entity
public class Customer {
// Fields and methods
}
Field Level Examples:
@Entity
public class Customer {
@Schema(
description = "Unique customer identifier in format CUSTXXX where XXX is 3 digits",
example = "CUST001",
pattern = "^CUST[0-9]{3}$",
required = true
)
@Id
private String id;
@Schema(
description = "Customer full name",
example = "Alice Johnson",
minLength = 1,
maxLength = 100,
required = true
)
private String name;
@Schema(
description = "Customer phone number (optional)",
example = "555-0123",
pattern = "^[0-9\\-\\+\\(\\)\\s]+$"
)
private String phone;
@Schema(
description = "Customer classification level",
allowableValues = {"INDIVIDUAL", "BUSINESS", "VIP", "PREMIUM"},
example = "VIP"
)
@Enumerated(EnumType.STRING)
private CustomerType type;
@Schema(description = "Customer address information")
@Embedded
private Address address;
}
Generated OpenAPI:
components:
schemas:
Customer:
type: object
description: Complete customer information including contact details and address
required:
- id
- name
properties:
id:
type: string
description: Unique customer identifier in format CUSTXXX where XXX is 3 digits
example: "CUST001"
pattern: "^CUST[0-9]{3}$"
name:
type: string
description: Customer full name
example: "Alice Johnson"
minLength: 1
maxLength: 100
phone:
type: string
description: Customer phone number (optional)
example: "555-0123"
pattern: "^[0-9\\-\\+\\(\\)\\s]+$"
type:
type: string
description: Customer classification level
enum: [INDIVIDUAL, BUSINESS, VIP, PREMIUM]
example: "VIP"
address:
$ref: '#/components/schemas/Address'
@Schema Properties Reference
| Property | Purpose | Example |
|---|---|---|
description |
Field explanation | "Customer full name" |
example |
Example value | "Alice Johnson" |
required |
Mark as required field | required = true |
pattern |
Regex validation | "^CUST[0-9]{3}$" |
minLength / maxLength |
String length limits | minLength = 1, maxLength = 100 |
minimum / maximum |
Numeric limits | minimum = "0", maximum = "150" |
allowableValues |
Enum values | {"INDIVIDUAL", "BUSINESS"} |
format |
Data format | "email", "date-time", "uuid" |
hidden |
Hide from documentation | hidden = true |
Organization and grouping annotations
@Tag
Purpose: Groups related endpoints together in Swagger UI.
Class Level Example:
@Tag(
name = "Customer Management",
description = "Operations for managing customer information including CRUD operations, search, and batch processing"
)
@RestController
public class CustomerAPIService {
// Controller methods
}
Method Level Example:
@Operation(
summary = "Search customers by criteria",
tags = {"Customer Search", "Advanced Operations"}
)
@GetMapping("/customers/search")
public PaginatedResponse<CustomerSummary> searchCustomers(/* parameters */) {
// Implementation
}
@Hidden
Purpose: Excludes endpoints or parameters from generated documentation.
Examples:
@Hidden // This endpoint won't appear in documentation
@GetMapping("/internal/debug")
public String debugEndpoint() {
// Internal endpoint
}
@GetMapping("/customers")
public PaginatedResponse<Customer> getCustomers(
@Parameter(hidden = true) // This parameter won't be documented
@RequestParam(required = false) String internalFlag
) {
// Implementation
}
Example and content annotations
@ExampleObject
Purpose: Provides detailed examples for requests and responses.
Single Example:
@ApiResponse(
responseCode = "200",
description = "Customer retrieved successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Customer.class),
examples = @ExampleObject(
name = "VIP Customer Example",
summary = "Example of a VIP customer response",
description = "Shows a complete VIP customer with all fields populated",
value = """
{
"name": "Alice Johnson",
"id": "CUST001",
"address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zipCode": "10001",
"country": "USA",
"formattedAddress": "123 Main St, New York, NY 10001, USA"
},
"phone": "555-0123",
"type": "VIP",
"city": "New York",
"businessCustomer": false,
"highValueCustomer": true
}
"""
)
)
)
Multiple Examples:
@ApiResponse(
responseCode = "400",
description = "Validation errors",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(
name = "Missing Required Field",
summary = "Required field validation error",
value = """
{
"message": "Customer name is required",
"timestamp": "2026-01-25T10:30:00.000Z",
"status": 400,
"error": "Bad Request"
}
"""
),
@ExampleObject(
name = "Invalid Format",
summary = "Format validation error",
value = """
{
"message": "Customer ID format must be CUSTXXX",
"timestamp": "2026-01-25T10:30:00.000Z",
"status": 400,
"error": "Bad Request"
}
"""
)
}
)
)
Configuration and global settings
OpenAPI Configuration Class
Purpose: Global API metadata and configuration.
Example:
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Customer Management APIs")
.version("1.0.0")
.description("REST API with H2 database integration")
.contact(new Contact()
.name("API Support")
.email("support@example.com"))
.license(new License()
.name("MIT License")
.url("https://opensource.org/licenses/MIT")))
.servers(List.of(
new Server()
.url("http://localhost:8080")
.description("Development Server"),
new Server()
.url("https://api.example.com")
.description("Production Server")
))
.addSecurityItem(new SecurityRequirement().addList("basicAuth"))
.components(new Components()
.addSecuritySchemes("basicAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("basic")
.description("HTTP Basic Authentication")));
}
}
Best practices for technical writers
1. Annotation Quality Guidelines
Effective Summaries:
// Good - Clear and concise
@Operation(summary = "Create a new customer")
// Bad - Too verbose or vague
@Operation(summary = "This endpoint creates a new customer record in the database")
@Operation(summary = "Create something")
Helpful Descriptions:
// Good - Provides context and business rules
@Operation(
summary = "Delete customer by ID",
description = """
Permanently removes a customer from the system. This operation cannot be undone.
Business Rules:
- Customer must exist
- Customer cannot have active orders
- Requires ADMIN role permissions
The customer's order history will be preserved but anonymized.
"""
)
// Bad - Just restates the summary
@Operation(
summary = "Delete customer",
description = "Deletes a customer"
)
Realistic Examples:
// Good - Realistic, helpful data
@Parameter(
description = "Customer ID",
example = "CUST001" // Follows actual format
)
// Bad - Generic or inconsistent data
@Parameter(
description = "Customer ID",
example = "123" // Doesn't match actual format
)
2. Collaboration with Developers
Enhancement Requests: When requesting annotation improvements, provide specific text:
// Current annotation
@Operation(summary = "Update customer")
// Suggested improvement
@Operation(
summary = "Update customer information",
description = """
Updates an existing customer's complete information. All fields in the request
will replace the current values - use PATCH for partial updates.
Business Rules:
- Customer ID cannot be changed
- Phone number format is validated
- Address information is required
"""
)
Testing Annotation Changes:
# After developers update annotations:
1. Restart the application
./mvnw spring-boot:run
2. Check generated documentation
curl http://localhost:8080/v3/api-docs.yaml > updated-spec.yaml
3. Compare with previous version
diff previous-spec.yaml updated-spec.yaml
4. Test examples in Swagger UI
open http://localhost:8080/swagger-ui/index.html
3. Documentation Maintenance
Monitor for Missing Annotations:
- New endpoints without @Operation
- Parameters without descriptions
- Missing response documentation
- Outdated examples
Version Control Integration:
# Track annotation changes alongside code
git log --oneline -- "*.java" | grep -i "annotation\|documentation\|openapi"
# Review documentation impact of code changes
git diff HEAD~1 -- src/main/java/*/controller/*.java
Common issues and solutions
Issue 1: Examples Don’t Match Reality
Problem: Annotation examples don’t work when tested.
Solution:
// Test examples against running API
curl -X POST http://localhost:8080/customer \
-H "Content-Type: application/json" \
-d '/* paste example from annotation */'
// Update annotation with working example
@Parameter(
example = "WORKING_EXAMPLE_THAT_WAS_TESTED"
)
Issue 2: Missing Business Context
Problem: Technical accuracy but poor user experience.
Solution:
// Add business context to descriptions
@Operation(
description = """
Technical: Updates customer record in database
Business: Use this when customer information changes (address, phone, etc.)
Workflow: Customer calls → Update info → Send confirmation email
"""
)
Issue 3: Inconsistent Documentation Style
Problem: Different developers use different annotation styles.
Solution: Create annotation guidelines document:
// Standard template for all endpoints
@Operation(
summary = "[Verb] [noun]", // e.g., "Create customer", "Delete order"
description = """
[What it does - one sentence]
Business Rules:
- Rule 1
- Rule 2
[Additional context if needed]
""",
tags = {"[Logical grouping]"}
)
This reference provides comprehensive coverage of SpringDoc annotations for generating OpenAPI documentation from Java code. Use it to understand current annotations, request improvements, and ensure documentation quality in code-first development workflows.