From software-development
REST API design conventions and best practices for TypeScript/Node.js. Loaded by backend-software-developer-agent when creating endpoints, designing request/response schemas, or structuring API routes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/software-development:api-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- Use **plural nouns**: `/users`, `/orders`, `/order-items`
/users, /orders, /order-items/users/:userId/orders/payment-methods| Method | Purpose | Success | Body |
|---|---|---|---|
| GET | Read resource(s) | 200 | Resource / array |
| POST | Create resource | 201 | Created resource + Location header |
| PUT | Full replace | 200 | Updated resource |
| PATCH | Partial update | 200 | Updated resource |
| DELETE | Remove | 204 | No body |
| Code | Meaning |
|---|---|
| 400 | Validation / bad input |
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
| 404 | Resource not found |
| 409 | Conflict (duplicate, stale update) |
| 422 | Semantically invalid (parseable but wrong) |
| 500 | Unexpected server error |
// Shared types
interface ApiError {
status: number;
code: string; // machine-readable: "VALIDATION_ERROR"
message: string; // human-readable
details?: Record<string, string[]>; // field-level errors
}
interface PaginatedResponse<T> {
data: T[];
meta: {
total: number;
page: number;
pageSize: number;
totalPages: number;
};
}
// Resource DTOs
interface CreateUserDto {
email: string;
name: string;
role: "admin" | "member";
}
interface UserResponse {
id: string;
email: string;
name: string;
role: "admin" | "member";
createdAt: string; // ISO 8601
}
Always return consistent error shape:
{
"status": 400,
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": {
"email": ["Must be a valid email address"],
"name": ["Required"]
}
}
Use query parameters: GET /users?page=2&pageSize=20
function paginate<T>(items: T[], total: number, page: number, pageSize: number): PaginatedResponse<T> {
return {
data: items,
meta: { total, page, pageSize, totalPages: Math.ceil(total / pageSize) },
};
}
For large datasets, prefer cursor-based pagination:
GET /events?cursor=abc123&limit=50
{ data: [...], meta: { nextCursor: "def456", hasMore: true } }
Prefer URL prefix for simplicity: /api/v1/users. Alternatives (Accept header) add complexity with little benefit for most projects.
import { Request, Response, NextFunction } from "express";
interface TypedRequest<B = unknown, Q = unknown> extends Request {
body: B;
query: Q;
}
const createUser = async (
req: TypedRequest<CreateUserDto>,
res: Response<UserResponse | ApiError>,
next: NextFunction
) => {
try {
const user = await userService.create(req.body);
res.status(201).json(user);
} catch (err) {
next(err);
}
};
router.post("/users", validateBody(createUserSchema), createUser);
import { FastifyRequest, FastifyReply } from "fastify";
app.post<{ Body: CreateUserDto; Reply: UserResponse }>(
"/users",
{ schema: { body: createUserSchema, response: { 201: userResponseSchema } } },
async (request, reply) => {
const user = await userService.create(request.body);
reply.status(201).send(user);
}
);
Content-Type: application/json consistently429 with Retry-After headernpx claudepluginhub bartekck/bartek-marketplace --plugin software-developmentGuides REST API design with standards for resource naming, versioning, and RFC 7807 error responses. Use when designing endpoints, pagination, or API structure.
REST API design patterns covering resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.
Guides RESTful API design and implementation: resource naming, HTTP methods, URL patterns, error responses, versioning, and core principles.