Automated summary
This skill helps build REST APIs with proper HTTP status codes, error handling using RFC 7807, request validation, and consistent response formatting.
Strengths
- Enforces appropriate HTTP status codes for different scenarios
- Adopts the RFC 7807 Problem Details standard for clear error responses
- Includes centralized error handling and input validation
Limitations
- Examples often rely on Next.js, requiring adaptation for other frameworks
- Does not cover GraphQL or non-REST API patterns
- Rate limiting and correlation IDs are outlined but not implemented in depth
Use this skill when building REST APIs that require robust error handling, input validation, and consistent response formats.
Do not use this skill for GraphQL APIs, simple microservices without detailed error needs, or when RFC 7807 overhead is undesirable.
Security analysis
SafeThe 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.
No concerns found
How this analysis is madeExamples
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.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.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: highparameter for thorough analysis.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/rest-api-development-mm5t8b9z/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/rest-api-development-mm5t8b9z' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/rest-api-development-mm5t8b9z/download' --output '.skill-downloads/rest-api-development-mm5t8b9z/SKILL.md.part' && mv '.skill-downloads/rest-api-development-mm5t8b9z/SKILL.md.part' '.skill-downloads/rest-api-development-mm5t8b9z/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Environment declared by the author: Claude Opus 4.5, Claude Code v2.x
Tools declared by the author (check availability in your agent): Read, Write, Edit, Bash
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
This file does not provide a usable name/description header for this native procedure. It remains available to download and inspect; use the manual method below.
Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/rest-api-development-mm5t8b9z/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.