API Design Principles
Designing a well-structured API is crucial for creating maintainable, scalable, and developer-friendly backend services. This guide covers essential principles and best practices for designing RESTful APIs with Express.js.
RESTful API Naming Conventions
Section titled “RESTful API Naming Conventions”Use kebab-case for URLs
Section titled “Use kebab-case for URLs”When creating endpoints with multiple words, use kebab-case (lowercase with hyphens):
✅ DO use kebab-case for multi-word resources:
GET /user-profilesGET /product-categoriesGET /order-items
❌ DON’T use camelCase or snake_case in URLs:
GET /userProfiles // Avoid camelCaseGET /product_categories // Avoid snake_case
Resource-Based URLs
Section titled “Resource-Based URLs”Structure your endpoints around resources (nouns) rather than actions (verbs):
✅ DO use resource-based naming:
// Resource collectionsGET /usersPOST /users
// Specific resourceGET /users/:idPUT /users/:idDELETE /users/:id
❌ DON’T use action-based naming:
// Avoid these patternsGET /getUsersPOST /createUserPUT /updateUser/:id
Use Verbs for Non-Resource URL
Section titled “Use Verbs for Non-Resource URL”While most endpoints should use nouns, there are exceptions for operation-based actions that don’t fit the standard CRUD model:
// Operation-based endpoints should use verbsPOST /users/:id/reset-passwordPOST /emails/sendGET /export-data
These are appropriate for actions that:
- Trigger a process or calculation
- Don’t directly map to a single resource
- Represent a state transition
Hierarchical Relationships
Section titled “Hierarchical Relationships”Express nested resources through URL paths:
// Get all posts by a specific userGET /users/:userId/posts
// Get a specific post from a specific userGET /users/:userId/posts/:postId
Pluralization
Section titled “Pluralization”Use consistent pluralization for resource collections:
✅ DO:
GET /users // List of usersGET /users/:id // Specific userGET /categories // List of categories
❌ DON’T mix singular and plural:
GET /user // Inconsistent with other endpointsGET /categories/:categoryId/post // Should be /posts
HTTP Methods
Section titled “HTTP Methods”Use appropriate HTTP methods for different operations:
Method | Purpose | Example |
---|---|---|
GET | Read/retrieve data | GET /products - Retrieve all products |
POST | Create new resources | POST /products - Create a new product |
PUT | Update existing resources (complete replacement) | PUT /products/:id - Replace entire product |
PATCH | Partial update | PATCH /products/:id - Update specific fields |
DELETE | Remove resources | DELETE /products/:id - Delete a product |
Query Parameters
Section titled “Query Parameters”Use camelCase for Parameters
Section titled “Use camelCase for Parameters”When defining query parameters and dynamic URL parameters, use camelCase for consistency:
// Correct query parameter naming with camelCaseGET /products?minPrice=10&maxPrice=100GET /users?firstName=John&lastName=DoeGET /orders?createdAfter=2023-01-01
// For dynamic URL parameters, also use camelCaseGET /users/:userId/posts/:postIdGET /orders/:orderId/items/:itemId
This maintains consistency with JavaScript naming conventions and makes frontend development smoother when parsing API responses and constructing requests.
Filtering, Sorting and Pagination
Section titled “Filtering, Sorting and Pagination”Filtering
Section titled “Filtering”Allow clients to filter collections:
GET /users?role=adminGET /products?category=electronics&inStock=true
Sorting
Section titled “Sorting”Enable sorting by specific fields:
GET /users?sort=lastNameGET /products?sort=-price // Descending order with - prefix
Pagination
Section titled “Pagination”Implement pagination to handle large result sets:
GET /products?page=2&limit=10
Response Structure
Section titled “Response Structure”Use camelCase for JSON Properties
Section titled “Use camelCase for JSON Properties”Consistently use camelCase for all JSON properties in your responses:
// Correct JSON property naming{ "userId": 123, "firstName": "John", "lastName": "Doe", "orderItems": [ { "productId": 456, "quantityOrdered": 2 } ],}
Include the Total Number of Resources in Your Response
Section titled “Include the Total Number of Resources in Your Response”When returning collections, especially paginated ones, always include metadata about the total count:
// Response with metadata including total count{ "statusCode": 200, "data": [ // Array of resources ], "meta": { "totalCount": 1357, "page": 2, "limit": 25, "totalPages": 55 }}
This helps clients to:
- Build proper pagination controls
- Show accurate counts to users
- Determine when they’ve reached the end of the collection
JSON Best Practices
Section titled “JSON Best Practices”- Be consistent with property naming
- Stick to camelCase throughout your entire API
- Use string format for complex identifiers
- UUIDs, large numbers should be strings to avoid precision issues
- Avoid nested objects deeper than 3 levels
- Deeply nested objects become hard to parse and understand
Standardized Response Format
Section titled “Standardized Response Format”Define a consistent response structure for all endpoints:
// Success response{ "statusCode": 200, "data": { // Resource data here }, "message": "Users retrieved successfully", "meta": { // Pagination, count, etc. (optional) "totalCount": 150, "page": 2, "limit": 20 }}
// Error response{ "statusCode": 400, "message": "Invalid user data provided", "path": "/api/v1/users", "timestamp": "2023-07-25T15:30:45.123Z"}
HTTP Status Codes
Section titled “HTTP Status Codes”Use appropriate HTTP status codes to indicate the outcome of requests:
Status Code | Description | When to Use |
---|---|---|
200 OK | Request succeeded | Successful GET, PUT, PATCH, or DELETE |
201 Created | Resource created | Successful POST that created a resource |
204 No Content | Success with no content to return | Successful DELETE or PUT with no response body |
400 Bad Request | Invalid request | Malformed request syntax, invalid parameters |
401 Unauthorized | Authentication required | Missing or invalid authentication |
403 Forbidden | Authenticated but not authorized | User lacks permission |
404 Not Found | Resource not found | Requested resource doesn’t exist |
409 Conflict | Request conflicts with current state | Duplicate resource, version conflicts, concurrent updates |
429 Too Many Requests | Rate limit exceeded | Client has sent too many requests in a given time period |
500 Internal Server Error | Server error | Exception occurred on the server |
API Versioning
Section titled “API Versioning”Versioning allows you to evolve your API without breaking existing clients.
URL-Based Versioning
Section titled “URL-Based Versioning”Include the version in the base path:
/api/v1/users/api/v2/users
Common Pitfalls
Section titled “Common Pitfalls”❌ Inconsistent naming patterns
- Mixing plural and singular resources
- Using different naming conventions across endpoints
❌ Exposing database details
- Returning internal error details to clients
- Exposing database IDs or structure directly
❌ Not using HTTP methods appropriately
- Using GET requests for data modification
- Using POST for everything
❌ Returning inappropriate status codes
- Returning 200 for errors
- Using 500 for client errors
❌ No rate limiting
- Leaving your API open to abuse
❌ Inconsistent data format
- Mixing date formats (use ISO 8601: YYYY-MM-DDTHH:MM:SSZ)
- Inconsistent number formatting
Conclusion
Section titled “Conclusion”Designing a well-structured API is essential for building scalable, maintainable applications. By following these principles, you can create APIs that are intuitive for developers to use, perform well, and can evolve over time.
When designing your API, remember these key points:
- Use resource-based URL naming with appropriate HTTP methods
- Implement consistent response structures and status codes
- Version your API to allow for evolution
- Include proper filtering, sorting, and pagination
- Implement security best practices from the beginning