home / skills / bitsoex / bitso-java / api-guidelines-rfc-39

api-guidelines-rfc-39 skill

/.claude/skills/api-guidelines-rfc-39

This skill helps design and review RFC-39 compliant Java REST APIs with standardized patterns for requests, responses, errors, pagination, versioning, and

npx playbooks add skill bitsoex/bitso-java --skill api-guidelines-rfc-39

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
7.8 KB
---
name: api-guidelines-rfc-39
description: >
  RFC-39 compliant API best practices for Java services. Covers request/response patterns,
  error handling, pagination, versioning, and authentication standards.
  Use when designing or reviewing REST APIs in Java services.
compatibility: Java projects using Spring Boot for REST APIs
metadata:
  version: "1.0.0"
  technology: java
  category: api
  tags:
    - java
    - api
    - rfc-39
    - rest
    - spring-boot
---

# API Guidelines (RFC-39)

RFC-39 compliant API best practices for Java services.

## When to use this skill

- Designing new REST API endpoints
- Reviewing API implementations for compliance
- Implementing error handling patterns
- Setting up pagination and filtering
- Configuring API versioning
- Implementing authentication and authorization

## Skill Contents

### Sections

- [When to use this skill](#when-to-use-this-skill) (L24-L32)
- [Quick Start](#quick-start) (L55-L93)
- [Request/Response Patterns](#requestresponse-patterns) (L94-L143)
- [Error Handling](#error-handling) (L144-L207)
- [Pagination](#pagination) (L208-L252)
- [Versioning](#versioning) (L253-L280)
- [Authentication](#authentication) (L281-L311)
- [References](#references) (L312-L317)
- [Related Rules](#related-rules) (L318-L321)
- [Related Skills](#related-skills) (L322-L327)

### Available Resources

**📚 references/** - Detailed documentation
- [response patterns](references/response-patterns.md)

---

## Quick Start

### 1. Controller Setup

```java
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;
    private final ResponseFactory responseFactory;

    @GetMapping("/{orderId}")
    public ResponseEntity<ApiResponse<OrderDto>> getOrder(
            @PathVariable String orderId) {
        OrderDto order = orderService.findById(orderId);
        return responseFactory.ok(order);
    }
}
```

### 2. Response Factory

```java
@Component
public class ResponseFactory {

    public <T> ResponseEntity<ApiResponse<T>> ok(T data) {
        return ResponseEntity.ok(ApiResponse.success(data));
    }

    public <T> ResponseEntity<ApiResponse<T>> created(T data) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(ApiResponse.success(data));
    }
}
```

## Request/Response Patterns

### Standard Response Envelope

```json
{
  "success": true,
  "data": { ... },
  "meta": {
    "requestId": "abc-123",
    "timestamp": "2026-01-27T12:00:00Z"
  }
}
```

### Java Implementation

```java
@Data
@Builder
public class ApiResponse<T> {
    private boolean success;
    private T data;
    private ApiError error;
    private ApiMeta meta;

    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
            .success(true)
            .data(data)
            .meta(ApiMeta.now())
            .build();
    }
}

@Data
@Builder
public class ApiMeta {
    private String requestId;
    private Instant timestamp;

    public static ApiMeta now() {
        return ApiMeta.builder()
            .requestId(MDC.get("requestId"))
            .timestamp(Instant.now())
            .build();
    }
}
```

## Error Handling

### Error Response Format

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": [
      {
        "field": "email",
        "message": "must be a valid email address"
      }
    ]
  },
  "meta": {
    "requestId": "abc-123",
    "timestamp": "2026-01-27T12:00:00Z"
  }
}
```

### Exception Handler

```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiResponse<Void>> handleNotFound(
            ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(ApiResponse.error("NOT_FOUND", ex.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<Void>> handleValidation(
            MethodArgumentNotValidException ex) {
        List<FieldError> details = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(e -> new FieldError(e.getField(), e.getDefaultMessage()))
            .toList();

        return ResponseEntity.badRequest()
            .body(ApiResponse.validationError(details));
    }
}
```

### Standard Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `VALIDATION_ERROR` | 400 | Invalid request parameters |
| `UNAUTHORIZED` | 401 | Missing or invalid authentication |
| `FORBIDDEN` | 403 | Insufficient permissions |
| `NOT_FOUND` | 404 | Resource not found |
| `CONFLICT` | 409 | Resource conflict |
| `RATE_LIMITED` | 429 | Too many requests |
| `INTERNAL_ERROR` | 500 | Unexpected server error |

## Pagination

### Request Parameters

| Parameter | Default | Description |
|-----------|---------|-------------|
| `page` | 0 | Page number (0-indexed) |
| `size` | 20 | Page size (max 100) |
| `sort` | - | Sort field and direction |

### Response Format

```json
{
  "success": true,
  "data": [...],
  "meta": {
    "pagination": {
      "page": 0,
      "size": 20,
      "totalElements": 150,
      "totalPages": 8,
      "hasNext": true,
      "hasPrevious": false
    }
  }
}
```

### Controller Example

```java
@GetMapping
public ResponseEntity<ApiResponse<List<OrderDto>>> listOrders(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "20") int size,
        @RequestParam(required = false) String sort) {

    Pageable pageable = PageRequest.of(page, Math.min(size, 100));
    Page<OrderDto> orders = orderService.findAll(pageable);

    return responseFactory.paginated(orders);
}
```

## Versioning

### URL Path Versioning (Preferred)

```java
@RestController
@RequestMapping("/api/v1/orders")
public class OrderV1Controller { }

@RestController
@RequestMapping("/api/v2/orders")
public class OrderV2Controller { }
```

### Deprecation Headers

```java
@GetMapping("/legacy-endpoint")
@Deprecated
public ResponseEntity<ApiResponse<Void>> legacyEndpoint() {
    return ResponseEntity.ok()
        .header("Deprecation", "true")
        .header("Sunset", "Sat, 01 Mar 2026 00:00:00 GMT")
        .header("Link", "</api/v2/new-endpoint>; rel=\"successor-version\"")
        .body(ApiResponse.success(null));
}
```

## Authentication

### WebAPI Annotation

```java
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    @GetMapping
    @WebAPI(AuthType.PRIVATE)  // Requires user authentication
    public ResponseEntity<...> listOrders() { }

    @GetMapping("/public-info")
    @WebAPI(AuthType.PUBLIC)   // No authentication required
    public ResponseEntity<...> getPublicInfo() { }

    @GetMapping("/internal")
    @WebAPI(AuthType.INTERNAL) // Service-to-service only
    public ResponseEntity<...> internalEndpoint() { }
}
```

### Authentication Types

| Type | Usage |
|------|-------|
| `PRIVATE` | User-authenticated endpoints |
| `PUBLIC` | Unauthenticated, public endpoints |
| `INTERNAL` | Service-to-service communication |

## References

| Reference | Description |
|-----------|-------------|
| [references/response-patterns.md](references/response-patterns.md) | Detailed response patterns |

## Related Rules

- [java-rest-api-guidelines](.cursor/rules/java-rest-api-guidelines/java-rest-api-guidelines.mdc) - REST API standards (RFC-30/RFC-39)

## Related Skills

| Skill | Purpose |
|-------|---------|
| [rest-api](.claude/skills/rest-api/SKILL.md) | REST API implementation |
| [grpc-services-rfc-33](.claude/skills/grpc-services-rfc-33/SKILL.md) | gRPC service standards |
<!-- AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY -->
<!-- Source: bitsoex/ai-code-instructions → java/skills/api-guidelines-rfc-39/SKILL.md -->
<!-- To modify, edit the source file and run the distribution workflow -->

Overview

This skill provides RFC-39 compliant API best practices tailored for Java services. It codifies request/response patterns, error handling, pagination, versioning, and authentication to use when designing or reviewing REST APIs. The guidance focuses on consistent envelopes, standard error codes, and practical controller patterns for maintainable services.

How this skill works

The skill defines a standard ApiResponse envelope with success, data, error, and meta sections (including requestId and timestamp). It provides Java examples for controllers, a ResponseFactory, global exception handling, pagination helpers, and versioning/deprecation approaches. Authentication types (PUBLIC, PRIVATE, INTERNAL) and annotations show when to require user auth or service-to-service access.

When to use it

  • Designing new REST endpoints for Java services
  • Reviewing existing APIs for RFC-39 compliance
  • Implementing consistent error handling and response envelopes
  • Adding pagination, sorting, or filtering to list endpoints
  • Introducing or enforcing API versioning and deprecation policies

Best practices

  • Always return a single ApiResponse envelope with meta.requestId and timestamp
  • Centralize response creation with a ResponseFactory to ensure consistency
  • Use a global exception handler to map exceptions to standard error codes
  • Limit page size server-side (max 100) and return pagination metadata
  • Prefer URL path versioning (e.g., /api/v1/) and send Deprecation/Sunset headers when retiring endpoints

Example use cases

  • Implementing GET /orders/{id} that returns ApiResponse<OrderDto> via ResponseFactory.ok()
  • Handling validation errors with MethodArgumentNotValidException to return VALIDATION_ERROR with field details
  • Listing resources with page, size, and sort parameters and returning pagination meta
  • Adding a new v2 controller while keeping a deprecated v1 endpoint that sends Deprecation and Link headers
  • Marking endpoints with @WebAPI(AuthType.PRIVATE) for user-authenticated requests and @WebAPI(AuthType.INTERNAL) for service-only APIs

FAQ

What should be included in the meta section?

Include requestId (from MDC) and a timestamp; pagination info goes under meta.pagination for list endpoints.

How do I handle validation errors?

Use a global exception handler to catch MethodArgumentNotValidException, map field errors to a details array, and return VALIDATION_ERROR with HTTP 400.