RESTful API Design
RESTful principles, resource design, and HTTP methods used in the API architecture.
RESTful API Design
This section covers the RESTful principles and design patterns used throughout the API architecture.
API Design Philosophy
Our API architecture follows REST principles with modern enhancements:
1. RESTful Design Principles
Resource-Based URLs
- Resources are nouns, not verbs
- Use plural forms for consistency
- Hierarchical structure for relationships
✅ Good Examples:
GET /api/products # Get all products
GET /api/products/123 # Get specific product
GET /api/products/123/reviews # Get product reviews
❌ Bad Examples:
GET /api/getProducts # Verb in URL
GET /api/product # Singular form
GET /api/products/get/123 # Mixed patterns
HTTP Methods for Actions
- GET: Retrieve resources (safe, idempotent)
- POST: Create new resources
- PUT: Update/replace resources (idempotent)
- PATCH: Partial updates
- DELETE: Remove resources (idempotent)
// Product API endpoints
interface ProductEndpoints {
'GET /api/products': {
query?: ProductFilters
response: PaginatedResponse<Product[]>
}
'POST /api/products': {
body: CreateProductRequest
response: Product
}
'GET /api/products/:id': {
params: { id: string }
response: Product
}
'PUT /api/products/:id': {
params: { id: string }
body: UpdateProductRequest
response: Product
}
'DELETE /api/products/:id': {
params: { id: string }
response: { success: boolean }
}
}
2. Stateless Communication
Each request contains all necessary information:
// Request includes authentication and context
const apiRequest = {
headers: {
'Authorization': 'Bearer jwt-token',
'Content-Type': 'application/json',
'X-Client-Version': '1.0.0'
},
body: {
// All required data for the operation
data: requestData,
metadata: {
timestamp: new Date().toISOString(),
requestId: generateUniqueId()
}
}
}
3. Consistent Response Formats
Standardized response structure across all endpoints:
// Success response format
interface ApiSuccessResponse<T> {
success: true
data: T
metadata: {
timestamp: string
requestId: string
version: string
}
pagination?: {
page: number
limit: number
total: number
hasNext: boolean
hasPrev: boolean
}
}
// Error response format
interface ApiErrorResponse {
success: false
error: {
code: string
message: string
details?: any
field?: string // For validation errors
}
metadata: {
timestamp: string
requestId: string
}
}
Resource Design Patterns
1. Collection and Resource URLs
// Collection operations
GET /api/products # List products
POST /api/products # Create product
// Resource operations
GET /api/products/:id # Get product
PUT /api/products/:id # Update product
PATCH /api/products/:id # Partial update
DELETE /api/products/:id # Delete product
2. Nested Resources
// Product categories (nested)
GET /api/products/:id/categories # Get product categories
POST /api/products/:id/categories # Add category to product
// Inventory for specific product
GET /api/products/:id/inventory # Get product inventory
PUT /api/products/:id/inventory # Update product inventory
// Product reviews
GET /api/products/:id/reviews # Get product reviews
POST /api/products/:id/reviews # Create product review
3. Query Parameters and Filtering
interface ProductQuery {
// Pagination
page?: number
limit?: number
// Sorting
sort?: 'name' | 'price' | 'created_at' | 'updated_at'
order?: 'asc' | 'desc'
// Filtering
category?: string
min_price?: number
max_price?: number
in_stock?: boolean
// Search
search?: string
// Field selection
fields?: string[] // ['id', 'name', 'price']
// Expansion
expand?: string[] // ['category', 'inventory']
}
// Example usage
GET /api/products?category=electronics&in_stock=true&sort=price&order=asc&page=1&limit=20
HTTP Status Codes
Success Codes
- 200 OK: Standard success response
- 201 Created: Resource created successfully
- 202 Accepted: Request accepted for processing
- 204 No Content: Success with no response body
Client Error Codes
- 400 Bad Request: Invalid request syntax or data
- 401 Unauthorized: Authentication required
- 403 Forbidden: Access denied
- 404 Not Found: Resource not found
- 409 Conflict: Resource conflict (duplicate)
- 422 Unprocessable Entity: Validation errors
Server Error Codes
- 500 Internal Server Error: Unexpected server error
- 502 Bad Gateway: Upstream service error
- 503 Service Unavailable: Service temporarily unavailable
// Status code usage examples
export const ApiResponses = {
// Success responses
ok: (data: any) => Response.json({ success: true, data }, { status: 200 }),
created: (data: any) => Response.json({ success: true, data }, { status: 201 }),
noContent: () => new Response(null, { status: 204 }),
// Error responses
badRequest: (message: string) =>
Response.json({ success: false, error: { code: 'BAD_REQUEST', message } }, { status: 400 }),
unauthorized: () =>
Response.json({ success: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } }, { status: 401 }),
forbidden: () =>
Response.json({ success: false, error: { code: 'FORBIDDEN', message: 'Access denied' } }, { status: 403 }),
notFound: (resource: string) =>
Response.json({ success: false, error: { code: 'NOT_FOUND', message: `${resource} not found` } }, { status: 404 })
}
Content Negotiation
Request/Response Headers
// Content type handling
const supportedContentTypes = [
'application/json',
'application/x-www-form-urlencoded',
'multipart/form-data' // For file uploads
]
// Accept header handling
const supportedResponseTypes = [
'application/json',
'text/csv', // For data exports
'application/pdf' // For reports
]
API Versioning
// Version strategies
const versioningStrategies = {
// URL path versioning (preferred)
urlPath: '/api/v1/products',
// Header versioning
header: {
'Accept': 'application/vnd.api+json;version=1',
'API-Version': '1.0'
},
// Query parameter versioning
queryParam: '/api/products?version=1'
}
Implementation Examples
Standard CRUD Operations
// products/route.ts
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const query = parseProductQuery(searchParams)
const products = await productService.list(query)
return ApiResponses.ok({
items: products.data,
pagination: products.pagination
})
} catch (error) {
return handleApiError(error)
}
}
export async function POST(request: Request) {
try {
const body = await request.json()
const validated = createProductSchema.parse(body)
const product = await productService.create(validated)
return ApiResponses.created(product)
} catch (error) {
if (error instanceof ZodError) {
return ApiResponses.badRequest('Invalid request data')
}
return handleApiError(error)
}
}
Resource-Specific Operations
// products/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const product = await productService.getById(params.id)
if (!product) {
return ApiResponses.notFound('Product')
}
return ApiResponses.ok(product)
} catch (error) {
return handleApiError(error)
}
}
export async function PUT(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const body = await request.json()
const validated = updateProductSchema.parse(body)
const product = await productService.update(params.id, validated)
return ApiResponses.ok(product)
} catch (error) {
return handleApiError(error)
}
}
API Documentation
OpenAPI/Swagger Integration
// api-docs/openapi.ts
export const productApiSpec = {
openapi: '3.0.0',
info: {
title: 'Smart Shelf API',
version: '1.0.0',
description: 'RESTful API for Smart Shelf inventory management'
},
paths: {
'/api/products': {
get: {
summary: 'List products',
parameters: [
{
name: 'page',
in: 'query',
schema: { type: 'integer', minimum: 1 }
},
{
name: 'limit',
in: 'query',
schema: { type: 'integer', minimum: 1, maximum: 100 }
}
],
responses: {
200: {
description: 'Products retrieved successfully',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/ProductListResponse' }
}
}
}
}
}
}
}
}
Best Practices
1. Resource Naming
- Use nouns for resources, not verbs
- Use plural forms consistently
- Keep URLs simple and predictable
- Use hyphens for multi-word resources
2. Error Handling
- Provide clear, actionable error messages
- Include error codes for programmatic handling
- Maintain consistent error response format
- Log errors appropriately for debugging
3. Performance
- Implement pagination for large datasets
- Support field selection to reduce payload size
- Use appropriate caching headers
- Implement request rate limiting
4. Security
- Validate all input data
- Use HTTPS for all communications
- Implement proper authentication and authorization
- Sanitize output to prevent XSS
This RESTful API design provides the foundation for building scalable, maintainable, and developer-friendly APIs that follow industry best practices and standards.