Claude-skill-registry api-design-restful
RESTful API design patterns, error handling, and documentation
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/api-design-restful" ~/.claude/skills/majiayu000-claude-skill-registry-api-design-restful && rm -rf "$T"
manifest:
skills/data/api-design-restful/SKILL.mdsafety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
- references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content
RESTful API Design
Core Principles
- Resource-Oriented - URLs represent nouns, not verbs
- Stateless - Each request contains all necessary information
- Consistent - Use standard HTTP methods and status codes
- Versioned - Support API evolution without breaking clients
URL Structure
# Collection resources GET /api/v1/users # List users POST /api/v1/users # Create user # Individual resources GET /api/v1/users/:id # Get user PUT /api/v1/users/:id # Replace user PATCH /api/v1/users/:id # Update user DELETE /api/v1/users/:id # Delete user # Nested resources GET /api/v1/users/:id/posts # User's posts POST /api/v1/users/:id/posts # Create post for user # Filtering, sorting, pagination GET /api/v1/users?status=active&sort=-createdAt&page=2&limit=20
Response Structure
Success Response
interface SuccessResponse<T> { success: true; data: T; meta?: { page?: number; limit?: number; total?: number; totalPages?: number; }; } // Example { "success": true, "data": { "id": "123", "name": "John" }, "meta": { "requestId": "abc-123" } }
Error Response
interface ErrorResponse { success: false; error: { code: string; // Machine-readable code message: string; // Human-readable message details?: unknown; // Field-level errors }; } // Example { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid request body", "details": { "email": "Invalid email format", "age": "Must be a positive number" } } }
HTTP Status Codes
// Success 200 OK // Successful GET, PUT, PATCH 201 Created // Successful POST 204 No Content // Successful DELETE // Client Errors 400 Bad Request // Validation errors 401 Unauthorized // Missing/invalid auth 403 Forbidden // Insufficient permissions 404 Not Found // Resource doesn't exist 409 Conflict // Duplicate/state conflict 422 Unprocessable // Semantic errors 429 Too Many Reqs // Rate limited // Server Errors 500 Internal Error // Unexpected server error 503 Unavailable // Service temporarily down
Express.js Implementation
import express from 'express'; const app = express(); // Async handler wrapper const asyncHandler = (fn: RequestHandler) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); // Controller const getUsers = asyncHandler(async (req, res) => { const { page = 1, limit = 20, status } = req.query; const { users, total } = await userService.findAll({ page, limit, status }); res.json({ success: true, data: users, meta: { page, limit, total, totalPages: Math.ceil(total / limit) } }); }); // Error handler middleware app.use((err, req, res, next) => { const status = err.status || 500; res.status(status).json({ success: false, error: { code: err.code || 'INTERNAL_ERROR', message: err.message || 'Something went wrong', ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) } }); });
Validation with Zod
import { z } from 'zod'; const createUserSchema = z.object({ email: z.string().email(), name: z.string().min(2).max(100), role: z.enum(['user', 'admin']).default('user'), }); // Middleware const validate = (schema: z.ZodSchema) => (req, res, next) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'Invalid request body', details: result.error.flatten().fieldErrors } }); } req.body = result.data; next(); }; app.post('/users', validate(createUserSchema), createUser);
Authentication
// JWT middleware const authenticate = asyncHandler(async (req, res, next) => { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { throw new ApiError(401, 'UNAUTHORIZED', 'Missing auth token'); } const payload = await verifyToken(token); req.user = payload; next(); }); // Role-based authorization const authorize = (...roles: string[]) => (req, res, next) => { if (!roles.includes(req.user.role)) { throw new ApiError(403, 'FORBIDDEN', 'Insufficient permissions'); } next(); }; app.delete('/users/:id', authenticate, authorize('admin'), deleteUser);
Rate Limiting
import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, standardHeaders: true, legacyHeaders: false, handler: (req, res) => { res.status(429).json({ success: false, error: { code: 'RATE_LIMITED', message: 'Too many requests, please try again later' } }); } }); app.use('/api/', limiter);
Best Practices
- Use plural nouns for resources (
not/users
)/user - Version your API from day one (
)/api/v1/ - Return appropriate status codes for every response
- Include request IDs in responses for debugging
- Document with OpenAPI/Swagger specification
- Implement pagination for list endpoints
- Use consistent error format across all endpoints
- Log all requests with correlation IDs