ai-observability
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when generating REST controllers, DTOs, success response contracts, pagination, HTTP status mapping, or API versioning. For RFC 9457 exception and error response formatting, use problem-details-rfc9457 unless the project explicitly requires a legacy error envelope.
Claude Code
$ npx -y skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions --agent claude-code
Codex
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a codex -y
Cursor
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a cursor -y
Gemini CLI
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a gemini-cli -y
GitHub Copilot
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a github-copilot -y
OpenCode
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a opencode -y
Windsurf
$ npx skills add rrezartprebreza/spring-boot-skills --skill rest-api-conventions -a windsurf -y
name: rest-api-conventions description: > Use when generating REST controllers, DTOs, success response contracts, pagination, HTTP status mapping, or API versioning. For RFC 9457 exception and error response formatting, use problem-details-rfc9457 unless the project explicitly requires a legacy error envelope.
Inspect existing controllers, tests and OpenAPI before choosing a response contract. Preserve the project's IDs, response shape and versioning strategy. Do not migrate unrelated endpoints while adding one route. The envelope below is an optional convention for a project that uses it; plain success DTOs are equally valid. A 204 response has no body.
Success and error formats are independent: an existing success envelope can coexist with RFC 9457 errors. Use one consistent error policy; choose the legacy error examples below only when the project already requires that format.
Example success envelope:
{
"success": true,
"data": { },
"error": null,
"timestamp": "2026-04-13T10:00:00Z"
}Error response:
{
"success": false,
"data": null,
"error": {
"code": "ORDER_NOT_FOUND",
"message": "Order with id 123 not found",
"details": []
},
"timestamp": "2026-04-13T10:00:00Z"
}@JsonInclude(JsonInclude.Include.NON_NULL)
public record ApiResponse<T>(
boolean success,
T data,
ApiError error,
Instant timestamp
) {
public static <T> ApiResponse<T> ok(T data) {
return new ApiResponse<>(true, data, null, Instant.now());
}
public static <T> ApiResponse<T> error(String code, String message) {
return new ApiResponse<>(false, null, new ApiError(code, message, List.of()), Instant.now());
}
}
public record ApiError(String code, String message, List<String> details) {}| Scenario | Status | |----------|--------| | GET — found | 200 | | POST — created resource | 201 | | PUT/PATCH — updated | 200 | | DELETE — deleted | 204 (no body) | | Validation failure | 400 | | Unauthenticated | 401 | | Forbidden | 403 | | Not found | 404 | | Conflict (duplicate) | 409 | | Unhandled server error | 500 |
GET /api/v1/orders → list (paginated)
POST /api/v1/orders → create
GET /api/v1/orders/{id} → get one
PUT /api/v1/orders/{id} → full update
PATCH /api/v1/orders/{id} → partial update
DELETE /api/v1/orders/{id} → delete
GET /api/v1/orders/{id}/items → nested resource{
"success": true,
"data": {
"content": [...],
"page": 0,
"size": 20,
"totalElements": 150,
"totalPages": 8,
"last": false
}
}Query params: `?page=0&size=20&sort=createdAt,desc`
Use Spring Data `Pageable` in controllers:
@GetMapping
public ApiResponse<PageResponse<OrderResponse>> list(Pageable pageable) {
return ApiResponse.ok(PageResponse.from(orderService.findAll(pageable).map(OrderResponse::from)));
}**Cap the page size.** A bare `Pageable` accepts `?size=100000` from any client — one request can drag your whole table into memory. Spring's default cap is 2000, still too high for most APIs:
spring:
data:
web:
pageable:
default-page-size: 20
max-page-size: 100 # requests above this are silently clampedThe compiled [PageResponse](templates/PageResponse.java) fixes the JSON pagination contract. Mapping entities to DTOs alone does not stabilize Spring Data `PageImpl` serialization. Spring Data's `org.springframework.data.web.PagedModel` is another option when its shape fits the API. Validate sort fields against an allowlist and add an ID tie-breaker to non-unique sorts.
// Extends ResponseEntityExceptionHandler: without it the Exception.class catch-all turns
// malformed JSON, 405, bad path variables and unknown URLs into 500. Full version, verified
// against Boot 3 and 4: templates/GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ApiResponse<Void>> handleNotFound(EntityNotFoundException ex) {
return ResponseEntity.status(404).body(ApiResponse.error("NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(AccessDeniedException.class) // @PreAuthorize denials surface here, not in the filter
public ResponseEntity<ApiResponse<Void>> handleAccessDenied(AccessDeniedException ex) {
return ResponseEntity.status(403).body(ApiResponse.error("FORBIDDEN", "Access denied"));
}
@Override // an @ExceptionHandler for this type would clash with the superclass at startup
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
List<String> details = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage()).toList();
return ResponseEntity.status(400).body(ApiResponse.validationError(details));
}
@Override // keep the framework's status + headers, return the envelope
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, Object body, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
return ResponseEntity.status(status).headeProduction-grade Claude Code and Codex skills for Spring Boot developers
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when versioning Spring MVC or WebFlux APIs in Spring Boot 3 / Spring Framework 6. Covers explicit URL, header, and media-type…
Use when introducing or correcting grouped Spring Boot configuration, typed property binding, validation, profiles or secret…
Use when packaging a Spring Boot 3 application as an OCI image or GraalVM native executable. Covers buildpacks, layered images,…
Use when evolving aggregate invariants, value objects or domain events in an existing DDD-style Spring Boot 3 application, or…
Use when implementing Kafka, RabbitMQ, Pulsar, or JMS producers and consumers in Spring Boot 3. Covers event contracts,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.