Jakarta EE compatible OpenAPI 3.x annotations for defining REST API specifications
—
Structured response documentation with status codes, content types, headers, request bodies, and linking between operations. This system provides comprehensive response definitions for REST API operations including success responses, error responses, and complex data flows.
Defines comprehensive API responses with status codes, content, headers, and links.
/**
* Describes API response with comprehensive metadata
* Applied to: METHOD, TYPE
* Repeatable: Yes
*/
@ApiResponse(
responseCode = "200", // HTTP response code (required)
description = "Successful response", // Response description (required)
content = { // Response content for different media types
@Content(
mediaType = "application/json",
schema = @Schema(implementation = User.class),
examples = {@ExampleObject(...)}
),
@Content(
mediaType = "application/xml",
schema = @Schema(implementation = User.class)
)
},
headers = { // Response headers
@Header(
name = "X-Rate-Limit-Remaining",
description = "Number of requests remaining",
schema = @Schema(type = "integer")
),
@Header(
name = "X-Rate-Limit-Reset",
description = "Rate limit reset timestamp",
schema = @Schema(type = "integer", format = "int64")
)
},
links = { // Links to related operations
@Link(
name = "getUserById",
operationId = "getUserById",
parameters = @LinkParameter(name = "userId", expression = "$response.body#/id")
)
},
extensions = {@Extension(...)}, // Custom extensions
ref = "#/components/responses/UserResponse" // Reference to component response
)Usage Examples:
// Success response with content
@GET
@Path("/{id}")
@Operation(summary = "Get user by ID")
@ApiResponse(
responseCode = "200",
description = "User found successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = User.class),
examples = @ExampleObject(
name = "userExample",
value = "{\"id\":123,\"name\":\"John Doe\",\"email\":\"john@example.com\"}"
)
),
headers = {
@Header(
name = "Last-Modified",
description = "Last modification timestamp",
schema = @Schema(type = "string", format = "date-time")
),
@Header(
name = "ETag",
description = "Entity tag for caching",
schema = @Schema(type = "string")
)
}
)
@ApiResponse(
responseCode = "404",
description = "User not found",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class)
)
)
public Response getUserById(@PathParam("id") Long id) {}
// Multiple success responses based on accept header
@GET
@Operation(summary = "Get user data in multiple formats")
@ApiResponse(
responseCode = "200",
description = "User data",
content = {
@Content(
mediaType = "application/json",
schema = @Schema(implementation = User.class)
),
@Content(
mediaType = "application/xml",
schema = @Schema(implementation = User.class)
),
@Content(
mediaType = "text/csv",
schema = @Schema(type = "string", description = "CSV representation")
)
}
)
public Response getUserData(@PathParam("id") Long id) {}
// Response with pagination headers
@GET
@Operation(summary = "List users with pagination")
@ApiResponse(
responseCode = "200",
description = "User list retrieved successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = UserList.class)
),
headers = {
@Header(
name = "X-Total-Count",
description = "Total number of users",
schema = @Schema(type = "integer")
),
@Header(
name = "X-Page-Number",
description = "Current page number",
schema = @Schema(type = "integer")
),
@Header(
name = "X-Page-Size",
description = "Number of items per page",
schema = @Schema(type = "integer")
),
@Header(
name = "Link",
description = "Pagination links (next, prev, first, last)",
schema = @Schema(type = "string")
)
}
)
public Response listUsers(
@QueryParam("page") Integer page,
@QueryParam("size") Integer size
) {}Container for multiple API response definitions on a single operation.
/**
* Container for multiple ApiResponse annotations
*/
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success", content = @Content(...)),
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(...)),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(...)),
@ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(...))
})Complete CRUD Response Examples:
@POST
@Operation(summary = "Create new user")
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "User created successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = User.class)
),
headers = {
@Header(
name = "Location",
description = "URL of the created user",
schema = @Schema(type = "string", format = "uri")
),
@Header(
name = "X-Request-ID",
description = "Request tracking ID",
schema = @Schema(type = "string", format = "uuid")
)
}
),
@ApiResponse(
responseCode = "400",
description = "Invalid user data provided",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ValidationError.class)
)
),
@ApiResponse(
responseCode = "409",
description = "User with this email already exists",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ConflictError.class)
)
),
@ApiResponse(
responseCode = "422",
description = "Unprocessable entity - validation failed",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ValidationError.class)
)
)
})
public Response createUser(@RequestBody CreateUserRequest request) {}
@PUT
@Path("/{id}")
@Operation(summary = "Update user")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "User updated successfully",
content = @Content(schema = @Schema(implementation = User.class))
),
@ApiResponse(
responseCode = "204",
description = "User updated successfully (no content returned)"
),
@ApiResponse(responseCode = "400", description = "Invalid update data"),
@ApiResponse(responseCode = "404", description = "User not found"),
@ApiResponse(responseCode = "409", description = "Update conflict (optimistic locking)")
})
public Response updateUser(@PathParam("id") Long id, @RequestBody UpdateUserRequest request) {}
@DELETE
@Path("/{id}")
@Operation(summary = "Delete user")
@ApiResponses({
@ApiResponse(responseCode = "204", description = "User deleted successfully"),
@ApiResponse(responseCode = "404", description = "User not found"),
@ApiResponse(
responseCode = "409",
description = "Cannot delete user with active dependencies",
content = @Content(schema = @Schema(implementation = ConflictError.class))
)
})
public Response deleteUser(@PathParam("id") Long id) {}Defines request body content with schema, examples, and validation requirements.
/**
* Defines request body for operations
* Applied to: METHOD, PARAMETER
*/
@RequestBody(
description = "User data for creation", // Request body description
required = true, // Whether request body is required
content = { // Content for different media types
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CreateUserRequest.class),
examples = {
@ExampleObject(
name = "basicUser",
summary = "Basic user creation",
value = "{\"name\":\"John Doe\",\"email\":\"john@example.com\"}"
),
@ExampleObject(
name = "adminUser",
summary = "Admin user creation",
value = "{\"name\":\"Admin User\",\"email\":\"admin@example.com\",\"role\":\"ADMIN\"}"
)
}
),
@Content(
mediaType = "application/xml",
schema = @Schema(implementation = CreateUserRequest.class)
)
},
extensions = {@Extension(...)}, // Custom extensions
ref = "#/components/requestBodies/CreateUserRequest" // Reference to component
)Usage Examples:
// JSON request body
@POST
@Operation(summary = "Create user")
@RequestBody(
description = "User data",
required = true,
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = CreateUserRequest.class),
examples = @ExampleObject(
name = "example",
value = "{\"name\":\"John\",\"email\":\"john@example.com\",\"age\":30}"
)
)
)
public Response createUser(CreateUserRequest request) {}
// Multiple content types
@POST
@Path("/upload")
@Operation(summary = "Upload user profile")
@RequestBody(
description = "User profile data",
required = true,
content = {
@Content(
mediaType = "application/json",
schema = @Schema(implementation = UserProfile.class)
),
@Content(
mediaType = "multipart/form-data",
schema = @Schema(implementation = UserProfileForm.class),
encoding = {
@Encoding(
name = "profileImage",
contentType = "image/png, image/jpeg",
headers = @Header(
name = "X-Image-Quality",
schema = @Schema(type = "string", allowableValues = {"low", "medium", "high"})
)
)
}
),
@Content(
mediaType = "application/x-www-form-urlencoded",
schema = @Schema(implementation = UserProfileForm.class)
)
}
)
public Response uploadProfile(UserProfileForm form) {}
// File upload request body
@POST
@Path("/documents")
@Operation(summary = "Upload document")
@RequestBody(
description = "Document file with metadata",
required = true,
content = @Content(
mediaType = "multipart/form-data",
encoding = {
@Encoding(
name = "file",
contentType = "application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document",
headers = {
@Header(
name = "Content-Disposition",
schema = @Schema(type = "string")
)
}
),
@Encoding(
name = "metadata",
contentType = "application/json"
)
}
)
)
public Response uploadDocument(
@FormParam("file") InputStream file,
@FormParam("metadata") DocumentMetadata metadata
) {}
// Optional request body
@PUT
@Path("/{id}/preferences")
@Operation(summary = "Update user preferences")
@RequestBody(
description = "User preferences (optional - missing fields will not be updated)",
required = false,
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = UserPreferences.class)
)
)
public Response updatePreferences(@PathParam("id") Long id, UserPreferences preferences) {}Defines headers returned in API responses with schemas and descriptions.
/**
* Defines response header
* Applied to: within response definitions
*/
@Header(
name = "X-Rate-Limit-Remaining", // Header name (required)
description = "Number of requests remaining in current window", // Description
required = false, // Whether header is always present
deprecated = false, // Deprecation status
explode = Explode.DEFAULT, // Explode array/object values
hidden = false, // Hide from documentation
schema = @Schema( // Header value schema
type = "integer",
minimum = "0",
example = "42"
),
array = @ArraySchema(...), // Array schema alternative
example = "100", // Example value
examples = {@ExampleObject(...)}, // Multiple examples
ref = "#/components/headers/RateLimit", // Reference to component header
extensions = {@Extension(...)} // Custom extensions
)Common Response Header Patterns:
// Rate limiting headers
@ApiResponse(
responseCode = "200",
description = "Success with rate limit info",
headers = {
@Header(
name = "X-RateLimit-Limit",
description = "Request limit per hour",
schema = @Schema(type = "integer", example = "1000")
),
@Header(
name = "X-RateLimit-Remaining",
description = "Requests remaining in current window",
schema = @Schema(type = "integer", example = "999")
),
@Header(
name = "X-RateLimit-Reset",
description = "Time when rate limit resets",
schema = @Schema(type = "integer", format = "int64", example = "1640995200")
)
}
)
// Caching headers
@ApiResponse(
responseCode = "200",
description = "Success with caching headers",
headers = {
@Header(
name = "ETag",
description = "Entity tag for caching",
schema = @Schema(type = "string", example = "\"abc123\"")
),
@Header(
name = "Last-Modified",
description = "Last modification time",
schema = @Schema(type = "string", format = "date-time")
),
@Header(
name = "Cache-Control",
description = "Cache control directives",
schema = @Schema(type = "string", example = "max-age=3600, must-revalidate")
)
}
)
// CORS headers
@ApiResponse(
responseCode = "200",
description = "Success with CORS headers",
headers = {
@Header(
name = "Access-Control-Allow-Origin",
description = "Allowed origins for CORS",
schema = @Schema(type = "string", example = "https://example.com")
),
@Header(
name = "Access-Control-Allow-Methods",
description = "Allowed HTTP methods",
schema = @Schema(type = "string", example = "GET, POST, PUT, DELETE")
),
@Header(
name = "Access-Control-Allow-Headers",
description = "Allowed request headers",
schema = @Schema(type = "string", example = "Content-Type, Authorization")
)
}
)
// Location header for created resources
@ApiResponse(
responseCode = "201",
description = "Resource created successfully",
headers = @Header(
name = "Location",
description = "URL of the created resource",
required = true,
schema = @Schema(type = "string", format = "uri", example = "https://api.example.com/users/123")
)
)
// Content disposition for file downloads
@ApiResponse(
responseCode = "200",
description = "File download",
content = @Content(
mediaType = "application/octet-stream",
schema = @Schema(type = "string", format = "binary")
),
headers = {
@Header(
name = "Content-Disposition",
description = "File download disposition",
schema = @Schema(type = "string", example = "attachment; filename=\"report.pdf\"")
),
@Header(
name = "Content-Length",
description = "File size in bytes",
schema = @Schema(type = "integer", format = "int64")
)
}
)Defines links between operations to describe workflows and related actions.
/**
* Defines link to related operation
* Applied to: within response definitions
*/
@Link(
name = "getUserById", // Link name (required)
operationRef = "#/paths/~1users~1{userId}/get", // Reference to operation
operationId = "getUserById", // Operation ID reference
parameters = { // Parameters to pass to linked operation
@LinkParameter(name = "userId", expression = "$response.body#/id")
},
requestBody = "$response.body#/userDetails", // Request body for linked operation
description = "Get the created user details", // Link description
server = @Server(...), // Alternative server for link
extensions = {@Extension(...)}, // Custom extensions
ref = "#/components/links/GetUserById" // Reference to component link
)
/**
* Link parameter definition
*/
@LinkParameter(
name = "userId", // Parameter name (required)
expression = "$response.body#/id" // Expression to get parameter value
)Link Expression Examples:
// Response body field
@LinkParameter(name = "userId", expression = "$response.body#/id")
// Response header value
@LinkParameter(name = "etag", expression = "$response.header.ETag")
// Query parameter from current request
@LinkParameter(name = "include", expression = "$request.query.include")
// Path parameter from current request
@LinkParameter(name = "version", expression = "$request.path.version")
// Constant value
@LinkParameter(name = "format", expression = "json")Usage Examples:
@POST
@Operation(summary = "Create user")
@ApiResponse(
responseCode = "201",
description = "User created successfully",
content = @Content(schema = @Schema(implementation = User.class)),
links = {
@Link(
name = "GetUserById",
description = "Get the created user",
operationId = "getUserById",
parameters = @LinkParameter(name = "id", expression = "$response.body#/id")
),
@Link(
name = "UpdateUser",
description = "Update the created user",
operationId = "updateUser",
parameters = @LinkParameter(name = "id", expression = "$response.body#/id")
),
@Link(
name = "DeleteUser",
description = "Delete the created user",
operationId = "deleteUser",
parameters = @LinkParameter(name = "id", expression = "$response.body#/id")
)
}
)
public Response createUser(@RequestBody CreateUserRequest request) {}
@GET
@Path("/{id}")
@Operation(operationId = "getUserById", summary = "Get user by ID")
@ApiResponse(
responseCode = "200",
description = "User retrieved successfully",
content = @Content(schema = @Schema(implementation = User.class)),
links = {
@Link(
name = "GetUserPosts",
description = "Get posts by this user",
operationId = "getPostsByUserId",
parameters = @LinkParameter(name = "userId", expression = "$response.body#/id")
),
@Link(
name = "GetUserProfile",
description = "Get user profile",
operationId = "getUserProfile",
parameters = @LinkParameter(name = "userId", expression = "$response.body#/id")
)
}
)
public Response getUserById(@PathParam("id") Long id) {}// Standard error schema
@Schema(description = "Standard error response")
public class ErrorResponse {
@Schema(description = "Error code", example = "USER_NOT_FOUND")
private String code;
@Schema(description = "Human-readable error message", example = "User with ID 123 not found")
private String message;
@Schema(description = "Request timestamp", format = "date-time")
private String timestamp;
@Schema(description = "Request tracking ID", format = "uuid")
private String requestId;
}
// Validation error schema
@Schema(description = "Validation error with field details")
public class ValidationError extends ErrorResponse {
@Schema(description = "Field-specific validation errors")
private List<FieldError> fieldErrors;
}
@Schema(description = "Individual field validation error")
public class FieldError {
@Schema(description = "Field name", example = "email")
private String field;
@Schema(description = "Invalid value", example = "not-an-email")
private String rejectedValue;
@Schema(description = "Validation message", example = "must be a valid email address")
private String message;
}@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "400",
description = "Bad Request - Invalid input data",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ValidationError.class),
examples = @ExampleObject(
name = "validationError",
value = "{\"code\":\"VALIDATION_ERROR\",\"message\":\"Invalid input\",\"fieldErrors\":[{\"field\":\"email\",\"message\":\"Invalid email format\"}]}"
)
)
),
@ApiResponse(
responseCode = "401",
description = "Unauthorized - Authentication required",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
),
@ApiResponse(
responseCode = "403",
description = "Forbidden - Insufficient permissions",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
),
@ApiResponse(
responseCode = "404",
description = "Not Found - Resource does not exist",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
),
@ApiResponse(
responseCode = "429",
description = "Too Many Requests - Rate limit exceeded",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)),
headers = {
@Header(name = "Retry-After", schema = @Schema(type = "integer", description = "Seconds to wait"))
}
),
@ApiResponse(
responseCode = "500",
description = "Internal Server Error",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})Install with Tessl CLI
npx tessl i tessl/maven-io-swagger-core-v3--swagger-annotations-jakarta