CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

90

Quality

90%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

This version of the tile failed moderation
Moderation pipeline encountered an internal error
Overview
Quality
Evals
Security
Files

exception-handling.mdplugins/developer-kit-java/skills/spring-boot-openapi-documentation/references/

Exception Documentation

Global Exception Handler

Comprehensive Exception Handler

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import io.swagger.v3.oas.annotations.Operation;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @Operation(hidden = true)
    public ErrorResponse handleBookNotFound(BookNotFoundException ex) {
        return new ErrorResponse("BOOK_NOT_FOUND", ex.getMessage());
    }

    @ExceptionHandler(ValidationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @Operation(hidden = true)
    public ErrorResponse handleValidation(ValidationException ex) {
        return new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
    }

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @Operation(hidden = true)
    public ErrorResponse handleAccessDenied(AccessDeniedException ex) {
        return new ErrorResponse("ACCESS_DENIED", "Insufficient permissions");
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @Operation(hidden = true)
    public ErrorResponse handleGeneric(Exception ex) {
        return new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred");
    }
}

Error Response Schema

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import java.util.List;

@Schema(description = "Standard error response")
public record ErrorResponse(
    @Schema(description = "Error code", example = "BOOK_NOT_FOUND")
    String code,

    @Schema(description = "Human-readable error message", example = "Book with ID 123 not found")
    String message,

    @Schema(description = "Additional error details")
    List<ValidationError> details,

    @Schema(description = "Error timestamp", example = "2024-01-15T10:30:00Z")
    LocalDateTime timestamp,

    @Schema(description = "Request path that caused the error", example = "/api/books/123")
    String path
) {
    public ErrorResponse(String code, String message) {
        this(code, message, List.of(), LocalDateTime.now(), "");
    }

    @Schema(description = "Validation error detail")
    public record ValidationError(
        @Schema(description = "Field name", example = "title")
        String field,

        @Schema(description = "Error message", example = "Title is required")
        String message
    ) {}
}

Document-Specific Error Responses

API Response with Error Codes

@Operation(
    summary = "Get book by ID",
    responses = {
        @ApiResponse(
            responseCode = "200",
            description = "Book found",
            content = @Content(schema = @Schema(implementation = Book.class))
        ),
        @ApiResponse(
            responseCode = "404",
            description = "Book not found",
            content = @Content(schema = @Schema(implementation = ErrorResponse.class))
        ),
        @ApiResponse(
            responseCode = "401",
            description = "Unauthorized",
            content = @Content(schema = @Schema(implementation = ErrorResponse.class))
        ),
        @ApiResponse(
            responseCode = "500",
            description = "Internal server error",
            content = @Content(schema = @Schema(implementation = ErrorResponse.class))
        )
    }
)
@GetMapping("/{id}")
public Book getBook(@PathVariable Long id) {
    return repository.findById(id).orElseThrow(() -> new BookNotFoundException(id));
}

Custom Error Response Examples

@Operation(
    summary = "Create book",
    responses = {
        @ApiResponse(
            responseCode = "201",
            description = "Book created"
        ),
        @ApiResponse(
            responseCode = "400",
            description = "Validation failed",
            content = @Content(
                schema = @Schema(implementation = ErrorResponse.class),
                examples = @ExampleObject(
                    value = """
                    {
                        "code": "VALIDATION_ERROR",
                        "message": "Validation failed",
                        "details": [
                            {"field": "title", "message": "Title is required"},
                            {"field": "isbn", "message": "Invalid ISBN format"}
                        ],
                        "timestamp": "2024-01-15T10:30:00Z",
                        "path": "/api/books"
                    }
                    """
                )
            )
        )
    }
)
@PostMapping
public Book createBook(@Valid @RequestBody Book book) {
    return repository.save(book);
}

Problem Details for HTTP APIs

RFC 7807 Problem Details

@Schema(description = "RFC 7807 Problem Details")
public record ProblemDetail(
    @Schema(description = "Problem type URI", example = "https://example.com/probs/book-not-found")
    String type,

    @Schema(description = "Short problem title", example = "Book Not Found")
    String title,

    @Schema(description = "HTTP status code", example = "404")
    int status,

    @Schema(description = "Detailed problem description", example = "Book with ID 123 does not exist")
    String detail,

    @Schema(description = "Instance identifier", example = "/api/books/123")
    String instance
) {}

@RestControllerAdvice
public class ProblemDetailExceptionHandler {

    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @Operation(hidden = true)
    public ProblemDetail handleNotFound(BookNotFoundException ex) {
        return new ProblemDetail(
            "https://example.com/probs/book-not-found",
            "Book Not Found",
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage(),
            "/api/books/" + ex.getId()
        );
    }
}

Document Constraint Violations

Bean Validation Error Documentation

@Schema(description = "Constraint violation detail")
public record ConstraintViolation(
    @Schema(description = "Invalid field", example = "title")
    String field,

    @Schema(description = "Constraint that failed", example = "NotBlank")
    String constraint,

    @Schema(description = "Error message", example = "must not be blank")
    String message,

    @Schema(description = "Invalid value", example = "null")
    String rejectedValue
) {}

@Schema(description = "Validation error response")
public record ValidationErrorResponse(
    @Schema(description = "Error code", example = "VALIDATION_FAILED")
    String code,

    @Schema(description = "Validation errors")
    List<ConstraintViolation> violations,

    @Schema(description = "Total number of violations", example = "2")
    int violationCount,

    @Schema(description = "Timestamp", example = "2024-01-15T10:30:00Z")
    LocalDateTime timestamp
) {}

Business Exception Documentation

Custom Business Exceptions

public class InsufficientStockException extends RuntimeException {
    private final Long bookId;
    private final int requested;
    private final int available;

    public InsufficientStockException(Long bookId, int requested, int available) {
        super(String.format("Insufficient stock for book %d: requested=%d, available=%d",
            bookId, requested, available));
        this.bookId = bookId;
        this.requested = requested;
        this.available = available;
    }

    // Getters...
}

@ExceptionHandler(InsufficientStockException.class)
@ResponseStatus(HttpStatus.CONFLICT)
@Operation(hidden = true)
public ErrorResponse handleInsufficientStock(InsufficientStockException ex) {
    return new ErrorResponse(
        "INSUFFICIENT_STOCK",
        String.format("Only %d copies available, %d requested", ex.getAvailable(), ex.getRequested()),
        Map.of(
            "bookId", ex.getBookId(),
            "requested", ex.getRequested(),
            "available", ex.getAvailable()
        )
    );
}

Exception Handling Best Practices

  1. Hide exception handlers from docs: Use @Operation(hidden = true)
  2. Document error responses: Include all possible error codes in @ApiResponse
  3. Use consistent error format: Standard error response structure
  4. Provide actionable messages: Help users understand and fix errors
  5. Include request ID: For tracing and support
  6. Don't expose sensitive data: Sanitize exception messages
  7. Use appropriate HTTP status codes: Follow HTTP semantics
@Schema(description = "Error response with request tracking")
public record ErrorResponse(
    String code,
    String message,
    Object details,
    LocalDateTime timestamp,
    String path,
    @Schema(description = "Request ID for support", example = "abc-123-xyz")
    String requestId
) {}

plugins

developer-kit-java

skills

README.md

CHANGELOG.md

context7.json

CONTRIBUTING.md

README_CN.md

README_ES.md

README_IT.md

README.md

tessl.json

tile.json