Designs RESTful APIs following industry best practices for resource naming, HTTP methods, status codes, and error handling. Use when designing new APIs, refactoring existing endpoints, or establishing API standards.
Applies RESTful API design principles when you're designing new APIs, refactoring endpoints, or establishing API standards. It triggers automatically when you mention API design, endpoints, or HTTP methods in your requests.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/bulk-operations.mdreferences/resource-patterns.mdREST (Representational State Transfer) is an architectural style for designing networked applications. This skill covers best practices for designing clean, consistent, and scalable REST APIs.
URLs represent resources (nouns), not actions (verbs).
# Good
GET /users
GET /users/123
GET /users/123/orders
# Bad
GET /getUsers
GET /getUserById?id=123
POST /createUser
| Method | Purpose | Idempotent | Safe |
|---|---|---|---|
| GET | Read resource | Yes | Yes |
| POST | Create resource | No | No |
| PUT | Replace resource | Yes | No |
| PATCH | Update resource | Yes | No |
| DELETE | Delete resource | Yes | No |
Each request contains all information needed. No server-side session state.
// Good - Token in header
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
// Bad - Relies on session
Cookie: session_id=abc123
# Collections (plural nouns)
/users
/products
/orders
# Single resource
/users/123
/products/abc
# Nested resources
/users/123/orders
/orders/456/items
# Avoid deep nesting (max 2 levels)
# Bad: /users/123/orders/456/items/789
# Good: /orders/456/items/789
# Filtering
GET /products?category=electronics&status=active
# Sorting
GET /products?sort=price&order=desc
GET /products?sort=-price # Prefix with - for descending
# Pagination
GET /products?page=2&limit=20
GET /products?offset=20&limit=20
GET /products?cursor=abc123&limit=20
# Field selection
GET /users/123?fields=id,name,email
# Search
GET /products?q=laptop
GET /products?search=laptop
# URL versioning (most common)
/api/v1/users
/api/v2/users
# Header versioning
Accept: application/vnd.api+json;version=1
# Query parameter
/users?version=1
// 200 OK - Successful GET, PUT, PATCH, DELETE
res.status(200).json({ user })
// 201 Created - Successful POST (with Location header)
res.status(201)
.header('Location', `/users/${user.id}`)
.json({ user })
// 204 No Content - Successful DELETE (no body)
res.status(204).send()
// 400 Bad Request - Invalid input
res.status(400).json({
error: 'VALIDATION_ERROR',
message: 'Invalid request body',
details: [
{ field: 'email', message: 'Invalid email format' }
]
})
// 401 Unauthorized - Missing/invalid auth
res.status(401).json({
error: 'UNAUTHORIZED',
message: 'Authentication required'
})
// 403 Forbidden - Authenticated but not allowed
res.status(403).json({
error: 'FORBIDDEN',
message: 'Insufficient permissions'
})
// 404 Not Found - Resource doesn't exist
res.status(404).json({
error: 'NOT_FOUND',
message: 'User not found'
})
// 409 Conflict - State conflict (duplicate, etc.)
res.status(409).json({
error: 'CONFLICT',
message: 'Email already registered'
})
// 422 Unprocessable Entity - Semantic errors
res.status(422).json({
error: 'UNPROCESSABLE_ENTITY',
message: 'Cannot process request'
})
// 429 Too Many Requests - Rate limited
res.status(429)
.header('Retry-After', '60')
.json({
error: 'RATE_LIMITED',
message: 'Too many requests',
retryAfter: 60
})
// 500 Internal Server Error
res.status(500).json({
error: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
requestId: req.id
})
// 503 Service Unavailable
res.status(503)
.header('Retry-After', '300')
.json({
error: 'SERVICE_UNAVAILABLE',
message: 'Service temporarily unavailable'
})
interface ApiError {
error: string // Machine-readable code
message: string // Human-readable message
details?: any[] // Additional details
requestId?: string // For debugging
documentation?: string // Link to docs
}
// Example
{
"error": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "age", "message": "Must be at least 18" }
],
"requestId": "req-abc123",
"documentation": "https://api.example.com/docs/errors#validation"
}
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 400,
"detail": "One or more fields failed validation",
"instance": "/users",
"errors": [
{ "pointer": "/email", "detail": "Invalid email format" }
]
}
// Request
GET /products?page=2&limit=20
// Response
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 150,
"totalPages": 8
}
}
// Request
GET /products?cursor=eyJpZCI6MTAwfQ&limit=20
// Response
{
"data": [...],
"pagination": {
"limit": 20,
"hasMore": true,
"nextCursor": "eyJpZCI6MTIwfQ"
}
}
Link: <https://api.example.com/products?cursor=abc>; rel="next",
<https://api.example.com/products?cursor=xyz>; rel="prev"
# Exact match
GET /products?status=active
# Multiple values
GET /products?status=active,pending
# Range
GET /products?price_min=10&price_max=100
GET /products?price[gte]=10&price[lte]=100
# Date range
GET /orders?created_after=2024-01-01&created_before=2024-12-31
# Full-text search
GET /products?q=laptop
# Nested filtering
GET /products?category.name=electronics
# Single field
GET /products?sort=price
GET /products?sort=-price # Descending
# Multiple fields
GET /products?sort=category,-price
GET /products?sort=category:asc,price:desc
# Default sort
GET /products # Defaults to created_at desc
// Success response
{
"data": { ... },
"meta": {
"requestId": "req-123",
"timestamp": "2024-01-15T10:30:00Z"
}
}
// Collection response
{
"data": [...],
"pagination": { ... },
"meta": { ... }
}
// Error response
{
"error": { ... }
}
// Use ISO 8601
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:45:00Z"
}
// String IDs (preferred for external APIs)
{ "id": "usr_abc123" }
// Numeric IDs
{ "id": 12345 }
Include links for discoverability:
{
"data": {
"id": "usr_123",
"name": "John Doe",
"links": {
"self": "/users/usr_123",
"orders": "/users/usr_123/orders",
"profile": "/users/usr_123/profile"
}
}
}
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000
Retry-After: 60
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100,
standardHeaders: true,
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
error: 'RATE_LIMITED',
message: 'Too many requests',
retryAfter: Math.ceil(req.rateLimit.resetTime / 1000)
})
}
})
// CORS
app.use(cors({
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}))
// Security headers
app.use(helmet())
// Content-Type enforcement
app.use((req, res, next) => {
if (req.method !== 'GET' && !req.is('application/json')) {
return res.status(415).json({
error: 'UNSUPPORTED_MEDIA_TYPE',
message: 'Content-Type must be application/json'
})
}
next()
})
// Client sends
POST /payments
Idempotency-Key: unique-request-id-123
// Server tracks and returns same response for duplicate requests
const idempotencyCache = new Map()
app.post('/payments', async (req, res) => {
const key = req.headers['idempotency-key']
if (key && idempotencyCache.has(key)) {
return res.json(idempotencyCache.get(key))
}
const result = await processPayment(req.body)
if (key) {
idempotencyCache.set(key, result)
}
res.status(201).json(result)
})
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.