API Design Guidelines
Overview
This skill provides guidelines for designing clean, consistent APIs. Apply these patterns when creating new endpoints, defining TypeScript interfaces, or reviewing API contracts.
Keywords: API design, REST, endpoints, TypeScript interfaces, data modeling, HTTP methods, response formats
REST Endpoint Design
URL Structure
/{resource} # Collection
/{resource}/{id} # Single resource
/{resource}/{id}/{sub-resource} # Nested resource
Good examples:
GET /users- List usersGET /users/123- Get user 123POST /users/123/orders- Create order for user 123
Avoid:
/getUsers- Don't use verbs in URLs/user/list- Don't use action words/users/123/getOrders- HTTP method implies action
HTTP Methods
| Method | Purpose | Idempotent | Body | | ------ | ---------------- | ---------- | ---- | | GET | Read resource | Yes | No | | POST | Create resource | No | Yes | | PUT | Replace resource | Yes | Yes | | PATCH | Update resource | No | Yes | | DELETE | Remove resource | Yes | No |
Response Codes
Success:
200- OK (GET, PUT, PATCH with body)201- Created (POST)204- No Content (DELETE, PUT/PATCH without body)
Client errors:
400- Bad Request (validation failed)401- Unauthorized (no/invalid auth)403- Forbidden (no permission)404- Not Found409- Conflict (duplicate, state conflict)422- Unprocessable Entity (semantic errors)
Server errors:
500- Internal Server Error503- Service Unavailable
Response Formats
Success Response
// Single resource
{
"data": {
"id": "123",
"name": "Example",
"createdAt": "2024-01-15T10:30:00Z"
}
}
// Collection
{
"data": [...],
"meta": {
"total": 100,
"page": 1,
"pageSize": 20
}
}
Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
TypeScript Interface Design
Naming Conventions
// Entities use PascalCase nouns
interface User {}
interface OrderItem {}
// Request/Response types include suffix
interface CreateUserRequest {}
interface GetUserResponse {}
// Params types for function arguments
interface ListUsersParams {}
Required vs Optional
// Required fields: always present
interface User {
id: string;
email: string;
createdAt: Date;
}
// Optional fields: may be undefined
interface CreateUserRequest {
email: string;
name?: string; // Optional
metadata?: Record<string, unknown>;
}
Discriminated Unions
Use for type-safe variants:
type PaymentMethod =
| { type: 'card'; cardNumber: string; expiry: string }
| { type: 'bank'; accountNumber: string; routingNumber: string }
| { type: 'crypto'; walletAddress: string };
// Usage
function processPayment(method: PaymentMethod) {
switch (method.type) {
case 'card':
// TypeScript knows cardNumber exists
return chargeCard(method.cardNumber);
case 'bank':
return initiateTransfer(method.accountNumber);
case 'crypto':
return sendCrypto(method.walletAddress);
}
}
Pagination
Offset-based (Simple)
interface PaginationParams {
page?: number; // Default: 1
pageSize?: number; // Default: 20, max: 100
}
interface PaginatedResponse<T> {
data: T[];
meta: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
}
Cursor-based (Scalable)
interface CursorParams {
cursor?: string;
limit?: number;
}
interface CursorResponse<T> {
data: T[];
meta: {
nextCursor: string | null;
hasMore: boolean;
};
}
Versioning
URL Versioning (Recommended)
/api/v1/users
/api/v2/users
Header Versioning
Accept: application/vnd.api+json; version=2
Common Patterns
Filtering
GET /users?status=active&role=admin
GET /orders?createdAfter=2024-01-01&createdBefore=2024-02-01
Sorting
GET /users?sort=createdAt:desc
GET /users?sort=lastName:asc,firstName:asc
Field Selection
GET /users?fields=id,name,email
GET /users/123?include=orders,profile
Security Considerations
- Authentication - Use Bearer tokens in Authorization header
- Rate limiting - Include
X-RateLimit-*headers - Input validation - Validate all request bodies with schemas
- Output encoding - Escape HTML in responses if rendered
- CORS - Configure allowed origins explicitly