Développement d'API REST

VérifiéSûr

Standardise le développement d'API REST avec des codes de statut HTTP appropriés, des réponses d'erreur conformes à la RFC 7807, une validation des entrées et un formatage cohérent des réponses. Utile lors de la création de routes API, de la gestion des erreurs, de la validation des entrées ou de la conception de réponses API.

Spar Skills Guide Bot
DeveloppementIntermédiaire
8002/06/2026
Claude Code
#api-development#rest-api#error-handling#rfc-7807

Recommandé pour

Notre avis

Ce skill permet de construire des API REST avec une gestion des erreurs appropriée, des codes de statut HTTP corrects, une validation des requêtes et un format de réponse standardisé (RFC 7807).

Points forts

  • Impose des codes de statut HTTP spécifiques plutôt que des codes génériques
  • Utilise le format RFC 7807 pour des réponses d'erreur claires et structurées
  • Centralise la gestion des erreurs et inclut la validation des entrées

Limites

  • Les exemples sont souvent basés sur Next.js, ce qui peut nécessiter une adaptation pour d'autres frameworks
  • Ne couvre pas les API GraphQL ni les protocoles non REST
  • La mise en place complète du rate limiting et des ID de corrélation est ébauchée mais pas approfondie
Quand l'utiliser

Utilisez ce skill lorsque vous développez des API REST qui nécessitent une gestion robuste des erreurs, une validation des entrées et des formats de réponse cohérents.

Quand l'éviter

Ne l'utilisez pas pour des API GraphQL, des microservices simples sans besoin de détails d'erreur avancés, ou dans des contextes où le format RFC 7807 est trop lourd.

Analyse de sécurité

Sûr
Score qualité88/100

The skill provides educational guidance on REST API development with error handling, status codes, validation, and response formatting. There are no instructions to execute destructive commands, exfiltrate data, or bypass safety measures. The allowed Bash tool is not misused; all code examples are safe and constructive.

Aucun point d'attention détecté

Exemples

User registration endpoint
Create a POST /api/users endpoint for user registration with input validation, proper HTTP status codes (201 for success, 400 for validation errors, 409 for duplicate email), and RFC 7807 error responses.
Centralized error handler
Implement a centralized error handler middleware for my Express.js API that converts all errors into RFC 7807 Problem Details format and includes a correlation ID.
Input validation middleware
Add input validation and sanitization to my existing REST API routes. Return 422 Unprocessable Entity with field-level error details in RFC 7807 format.

name: "API Development" description: "Build REST APIs with proper error handling, status codes, request validation, response formatting, and rate limiting. Apply when creating API routes, handling errors, validating input, or designing API responses." allowed-tools: Read, Write, Edit, Bash version: 1.1.0 compatibility: Claude Opus 4.5, Claude Code v2.x updated: 2026-01-24

API Development

Systematic REST API development with error handling, validation, and consistent response formats.

Overview

This Skill enforces:

  • HTTP status codes (appropriate, not overused)
  • RFC 7807 Problem Details for errors
  • Input validation and sanitization
  • Consistent response formatting
  • Request correlation IDs
  • Rate limiting
  • Security-first error messages
  • Centralized error handling

Apply when building API routes, handling errors, or designing responses.

HTTP Status Codes

Status Code Categories

| Range | Purpose | Common Examples | |-------|---------|-----------------| | 200-299 | Success | 200 OK, 201 Created, 204 No Content | | 300-399 | Redirection | 301 Moved Permanently, 302 Found | | 400-499 | Client Errors | 400 Bad Request, 401 Unauthorized, 404 Not Found | | 500-599 | Server Errors | 500 Internal Error, 503 Service Unavailable |

Correct Status Codes

// ✅ GOOD: Specific status codes
200  // GET: Resource retrieved
201  // POST: Resource created
204  // DELETE: Resource deleted (no content)
400  // Bad Request: Validation failed
401  // Unauthorized: Not authenticated
403  // Forbidden: Authenticated but no permission
404  // Not Found: Resource doesn't exist
409  // Conflict: Duplicate email
422  // Unprocessable Entity: Semantic error
429  // Too Many Requests: Rate limited
500  // Internal Server Error: Server bug

// ❌ BAD: Vague status codes
200  // Success response for everything
500  // Error response for everything
200  // Returned even when validation failed

Error Response Format (RFC 7807)

Problem Details Structure

// RFC 7807 Problem Details
type ProblemDetails = {
  type: string;        // URL to error type documentation
  title: string;       // Short error title
  status: number;      // HTTP status code
  detail: string;      // Specific error details
  instance?: string;   // Request ID for tracking
  errors?: Record<string, string[]>;  // Field-level errors
};

Implementation

// lib/errors.ts
export class ApiError extends Error {
  constructor(
    public status: number,
    public title: string,
    public detail: string,
    public type: string = 'about:blank',
    public errors?: Record<string, string[]>
  ) {
    super(detail);
    this.name = 'ApiError';
  }

  toJSON() {
    return {
      type: this.type,
      title: this.title,
      status: this.status,
      detail: this.detail,
      instance: this.instance,
      ...(this.errors && { errors: this.errors })
    };
  }
}

Error Responses

// ✅ GOOD: RFC 7807 format
{
  "type": "https://api.example.com/errors/validation-failed",
  "title": "Validation Failed",
  "status": 400,
  "detail": "The request body contains invalid data",
  "instance": "req-12345",
  "errors": {
    "email": ["Invalid email format"],
    "age": ["Must be >= 18"]
  }
}

// ✅ GOOD: Unauthorized (no sensitive details)
{
  "type": "https://api.example.com/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required",
  "instance": "req-12346"
}

// ❌ BAD: Leaks internal details
{
  "error": "User not found in database",
  "stack": "Error: query failed at line 42..."
}

// ❌ BAD: Not structured
{
  "message": "Something went wrong"
}

Centralized Error Handler

// middleware/error-handler.ts
import { NextRequest, NextResponse } from 'next/server';
import { ApiError } from '@/lib/errors';

export function errorHandler(error: unknown) {
  const requestId = crypto.randomUUID();

  // Log error (internal, never exposed)
  console.error(`[${requestId}] Error:`, error);

  // ApiError (predictable)
  if (error instanceof ApiError) {
    return NextResponse.json(
      {
        type: error.type,
        title: error.title,
        status: error.status,
        detail: error.detail,
        instance: requestId,
        ...(error.errors && { errors: error.errors })
      },
      { status: error.status }
    );
  }

  // Validation error
  if (error instanceof ZodError) {
    return NextResponse.json(
      {
        type: 'https://api.example.com/errors/validation-failed',
        title: 'Validation Failed',
        status: 400,
        detail: 'The request body contains invalid data',
        instance: requestId,
        errors: error.flatten().fieldErrors
      },
      { status: 400 }
    );
  }

  // Unknown error (generic message)
  return NextResponse.json(
    {
      type: 'https://api.example.com/errors/internal-server-error',
      title: 'Internal Server Error',
      status: 500,
      detail: 'An unexpected error occurred',
      instance: requestId
    },
    { status: 500 }
  );
}

Using Error Handler

// app/api/users/route.ts
import { errorHandler } from '@/middleware/error-handler';

export async function POST(request: Request) {
  try {
    const body = await request.json();

    // Validate
    const validated = CreateUserSchema.parse(body);

    // Check duplicate
    const existing = await db.user.findUnique({
      where: { email: validated.email }
    });

    if (existing) {
      throw new ApiError(
        409,
        'Conflict',
        'A user with this email already exists',
        'https://api.example.com/errors/duplicate-email'
      );
    }

    // Create
    const user = await db.user.create({ data: validated });

    return new Response(JSON.stringify(user), {
      status: 201,
      headers: { 'Content-Type': 'application/json' }
    });
  } catch (error) {
    return errorHandler(error);
  }
}

Input Validation

Schema Validation

import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email('Invalid email format'),
  name: z.string().min(1, 'Name required').max(255),
  age: z.number().int().min(0).max(150),
  role: z.enum(['admin', 'user', 'guest']).default('user')
});

// Validate request
const validated = CreateUserSchema.parse(body);

Sanitization

import DOMPurify from 'isomorphic-dompurify';

const sanitized = {
  ...validated,
  name: DOMPurify.sanitize(validated.name)
};

Rate Limiting

import rateLimit from 'express-rate-limit';

// General rate limiter
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 100,                   // 100 requests per window
  message: 'Too many requests, please try again later',
  standardHeaders: true,      // Return rate limit info in headers
  legacyHeaders: false
});

// Auth rate limiter (stricter)
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,                      // 5 attempts
  skipSuccessfulRequests: true // Don't count successful logins
});

app.post('/login', authLimiter, loginHandler);
app.use('/api/', limiter);

Response Formatting

Success Response

// ✅ GOOD: Consistent response
export async function GET(request: Request) {
  const users = await db.user.findMany();

  return NextResponse.json({
    status: 'success',
    data: users,
    meta: {
      count: users.length,
      timestamp: new Date().toISOString()
    }
  });
}

// ✅ GOOD: Paginated response
export async function GET(request: Request) {
  const page = parseInt(request.nextUrl.searchParams.get('page') || '1');
  const limit = parseInt(request.nextUrl.searchParams.get('limit') || '20');
  const offset = (page - 1) * limit;

  const [users, total] = await Promise.all([
    db.user.findMany({ skip: offset, take: limit }),
    db.user.count()
  ]);

  return NextResponse.json({
    status: 'success',
    data: users,
    meta: {
      pagination: {
        page,
        limit,
        total,
        pages: Math.ceil(total / limit)
      }
    }
  });
}

Request Correlation

// middleware/correlation-id.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const correlationId = 
    request.headers.get('x-correlation-id') || 
    crypto.randomUUID();

  const response = NextResponse.next();
  response.headers.set('x-correlation-id', correlationId);

  return response;
}

// Include in logs
console.log(`[${correlationId}] User created:`, user);

// Client can track requests
fetch('/api/users', {
  headers: { 'x-correlation-id': myRequestId }
});

Anti-Patterns

// ❌ BAD: Leaking stack traces
{
  "error": "Cannot read property 'id' of undefined at getUserData (line 42)",
  "stack": "Error: ...\nat app.js:42..."
}

// ❌ BAD: Generic error message
{
  "error": "Something went wrong"
}

// ❌ BAD: No rate limiting
// Anyone can hammer API endpoint

// ❌ BAD: Overusing 500
// Always return 500 for any error

// ❌ BAD: No validation
const user = await db.user.create(request.body);
// Raw user input!

Verification Before Production

  • [ ] HTTP status codes specific and appropriate
  • [ ] Error responses RFC 7807 compliant
  • [ ] No stack traces or sensitive data exposed
  • [ ] Input validated on server side
  • [ ] Input sanitized before storage
  • [ ] Rate limiting configured
  • [ ] Correlation IDs for request tracking
  • [ ] Error messages user-friendly (not technical)
  • [ ] Centralized error handler
  • [ ] Response format consistent

Integration with Project Standards

Enforces security and usability:

  • S-1: No sensitive data in errors
  • C-10: Input validated
  • AP-8: Validation on server side

Resources

  • RFC 7807 Problem Details: https://tools.ietf.org/html/rfc7807
  • HTTP Status Codes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
  • Express Rate Limiting: https://github.com/nfriedly/express-rate-limit

Last Updated: January 24, 2026 Compatibility: Claude Opus 4.5, Claude Code v2.x Status: Production Ready

January 2026 Update: This skill is compatible with Claude Opus 4.5 and Claude Code v2.x. For complex tasks, use the effort: high parameter for thorough analysis.

Skills similaires