Introduction to API Endpoint Design
The design of your API endpoints plays a critical role in how users perceive and interact with your API. Well-designed endpoints make your API intuitive, easy to use, and maintainable. They establish patterns that users can follow to predict how to interact with resources they haven't encountered before.
What is an API Endpoint?
An API endpoint is a specific URL within your API that represents either a resource or an action. Each endpoint accepts specific HTTP methods and returns specific responses. In RESTful APIs, endpoints are designed around resources and follow consistent patterns.
The Library Catalog Analogy
Designing API endpoints is like organizing a library catalog system:
- Main Categories (Books, Journals, Media) = Top-level resources (/users, /products, /orders)
- Specific Items (Book #123) = Individual resources (/users/123)
- Book's Chapters = Nested resources (/users/123/posts)
- Card Catalog = Directory of available endpoints
- Naming Conventions = Consistent naming patterns make it easy to find what you're looking for
- Cross-References = Relationships between resources
- Catalog Filters = Query parameters to filter, sort, or paginate
Just as a well-organized library catalog helps visitors quickly find what they need without having to understand the entire system, well-designed API endpoints help developers intuitively navigate your API.
Why Endpoint Design Matters
- Usability: Intuitive endpoints reduce the learning curve for API consumers
- Discoverability: Consistent patterns help users predict how to interact with new resources
- Maintenance: Well-structured endpoints are easier to maintain and extend
- Documentation: Logical organization simplifies documentation
- Evolution: Good design allows the API to evolve without breaking existing clients
- Developer Experience: Pleasant, predictable APIs encourage adoption and reduce support burden
Resource Naming Best Practices
The foundation of good API endpoint design is consistent, intuitive resource naming. How you name your resources significantly impacts the usability of your API.
Use Nouns, Not Verbs
RESTful APIs use HTTP methods (GET, POST, PUT, DELETE) to indicate the action, so endpoint URLs should represent resources (nouns), not actions (verbs).
| Poor Design | Good Design | Notes |
|---|---|---|
GET /getUsers |
GET /users |
Let the HTTP method indicate the action (GET) |
POST /createOrder |
POST /orders |
POST inherently means creation |
PUT /updateProduct/123 |
PUT /products/123 |
PUT inherently means update |
DELETE /removeComment/456 |
DELETE /comments/456 |
DELETE inherently means removal |
Plural vs. Singular Resource Names
There are two common approaches to naming resources: plural and singular. The most important thing is to be consistent throughout your API.
| Approach | Example | Pros | Cons |
|---|---|---|---|
| Plural |
/users/users/123
|
|
|
| Singular |
/user/user/123
|
|
|
Recommendation
Use plural resource names (e.g., /users, /products, /orders). This approach is more widely adopted in the industry and creates a more intuitive distinction between collections (/users) and specific items (/users/123).
Resource Naming Conventions
Adopt a consistent naming convention for your resources and stick to it throughout your API.
| Aspect | Recommendation | Example |
|---|---|---|
| Case Style |
Choose one:
|
/product-categories/product_categories/productCategories
|
| Compound Words | Use meaningful and complete words instead of abbreviations or truncations |
/products (not /prods)/customers (not /custs)
|
| Domain Model Alignment | Resource names should align with your domain model and be recognizable to domain experts |
/invoices not /bills(if "invoice" is the domain term) |
| Avoid Technical Details | Hide implementation details from the API consumer |
/articles not /article-table
|
Example: E-commerce API Resources
/products: All products/products/123: Specific product/categories: All categories/categories/electronics: Specific category/customers: All customers/customers/456: Specific customer/orders: All orders/orders/789: Specific order/shopping-carts: All shopping carts/shopping-carts/abc: Specific shopping cart
URL Structure and Resource Hierarchy
The structure of your API URLs should reflect logical relationships between resources and guide users through your API's domain model.
Resource Hierarchy
Resources often have hierarchical relationships that can be reflected in the URL structure. There are several approaches to representing these relationships.
| Approach | Example | When to Use |
|---|---|---|
| Flat Structure |
/orders/order-items
|
When resources are largely independent or when deep nesting would create overly complex URLs |
| Nested Resources |
/orders/{orderId}/items/orders/{orderId}/items/{itemId}
|
When resources have a strong parent-child relationship (containment) or when scoping is important |
| Hybrid Approach |
/orders/{orderId}/items (for items in an order)/items/{itemId} (for direct item access)
|
When both scoped access and direct access are needed |
Nested Resources
Nested resources represent parent-child relationships and provide scoped access to resources.
// Example: Blog API with nested resources
GET /users/{userId}/posts // Get all posts by a specific user
GET /users/{userId}/posts/{postId} // Get a specific post by a specific user
POST /users/{userId}/posts // Create a new post for a specific user
GET /posts/{postId}/comments // Get all comments for a specific post
POST /posts/{postId}/comments // Add a comment to a specific post
GET /posts/{postId}/comments/{commentId} // Get a specific comment on a specific post
Nesting Depth Guidelines
Limit nesting to 2-3 levels deep to keep URLs manageable. If you find yourself going deeper, consider these alternatives:
- Flatten some relationships into their own top-level resources
- Use query parameters to filter resources instead of deep nesting
- Implement a hybrid approach with both nested and direct access
// Instead of deep nesting like this:
GET /users/{userId}/posts/{postId}/comments/{commentId}/replies/{replyId}
// Consider flattening:
GET /replies/{replyId}
GET /comments/{commentId}/replies
// Or using query parameters:
GET /replies?commentId={commentId}
Path Parameters
Path parameters are used to identify specific resources or sub-resources in the URL path.
// Path parameters examples
GET /products/{productId} // {productId} is a path parameter
GET /users/{userId}/orders/{orderId} // {userId} and {orderId} are path parameters
Path Parameter Naming
Choose descriptive and consistent names for path parameters that reflect the resource type:
- Use the singular form of the resource name:
{userId}, not{usersId} - Be consistent with parameter naming across endpoints
- Include the resource type in the parameter name:
{productId}, not just{id} - Consider using semantic identifiers when appropriate:
{username},{slug}
Resource Hierarchy Example: E-commerce API
// Customers and their data
GET /customers // List all customers
GET /customers/{customerId} // Get a specific customer
GET /customers/{customerId}/addresses // Get all addresses for a customer
GET /customers/{customerId}/addresses/{addressId} // Get a specific address
GET /customers/{customerId}/payment-methods // Get all payment methods
GET /customers/{customerId}/orders // Get all orders for a customer
// Products and their organization
GET /products // List all products
GET /products/{productId} // Get a specific product
GET /categories // List all categories
GET /categories/{categoryId} // Get a specific category
GET /categories/{categoryId}/products // Get products in a category
// Orders and their items
GET /orders // List all orders
GET /orders/{orderId} // Get a specific order
GET /orders/{orderId}/items // Get items in an order
GET /orders/{orderId}/shipments // Get shipments for an order
GET /orders/{orderId}/payments // Get payments for an order
// Flattened access for direct operations
GET /addresses/{addressId} // Direct access to an address
GET /order-items/{itemId} // Direct access to an order item
Query Parameters for Filtering, Sorting, and Pagination
Query parameters allow clients to customize their requests without changing the endpoint URL structure. They're ideal for operations that don't change the nature of the resource being accessed.
Filtering
Filtering allows clients to request a subset of resources based on specific criteria.
// Basic filtering examples
GET /products?category=electronics // Filter products by category
GET /users?status=active // Get only active users
GET /orders?customerId=123 // Get orders for a specific customer
GET /events?date=2025-05-01 // Get events on a specific date
// Multiple filters
GET /products?category=electronics&minPrice=100&maxPrice=500
GET /users?status=active&role=admin
// Array values
GET /products?categories=electronics&categories=accessories
// Range filtering
GET /products?price_gte=100&price_lte=500
GET /events?date_gte=2025-05-01&date_lte=2025-05-31
// Search
GET /products?search=wireless+headphones
GET /articles?title_contains=javascript
Filtering Best Practices
- Use descriptive parameter names that indicate the field being filtered
- Use standardized operators like _eq, _neq, _gt, _gte, _lt, _lte, _in, _contains
- Document all supported filters clearly in your API documentation
- Consider a query language for complex filtering requirements
- Support both exact and partial matching where appropriate
- Be mindful of performance when allowing complex filters
Sorting
Sorting allows clients to order the results based on specific fields.
// Basic sorting
GET /products?sort=price // Sort by price (default ascending)
GET /users?sort=lastName // Sort by last name
// Specifying sort direction
GET /products?sort=price_desc // Sort by price in descending order
GET /users?sort=lastName_asc // Sort by last name in ascending order
// Multiple sort criteria
GET /users?sort=lastName_asc,firstName_asc // Sort by last name, then first name
GET /products?sort=category_asc,price_desc // Sort by category, then price (descending)
Sorting Best Practices
- Use a consistent sort parameter name (e.g., 'sort' or 'order')
- Specify direction with suffixes like _asc or _desc
- Support multiple sort criteria separated by commas
- Document default sort order for each resource
- Consider performance implications for sorting on non-indexed fields
Pagination
Pagination allows clients to request a subset of results to improve performance and usability.
// Offset-based pagination
GET /products?limit=20&offset=0 // First page (items 0-19)
GET /products?limit=20&offset=20 // Second page (items 20-39)
// Page-based pagination
GET /products?page=1&perPage=20 // First page
GET /products?page=2&perPage=20 // Second page
// Cursor-based pagination
GET /products?limit=20&after=abc123 // Items after the cursor 'abc123'
GET /products?limit=20&before=xyz789 // Items before the cursor 'xyz789'
Pagination Best Practices
- Always implement pagination for collection endpoints
- Set reasonable defaults for page size (e.g., 20-50 items)
- Set a maximum page size to prevent performance issues
- Include pagination metadata in the response
- Consider cursor-based pagination for large datasets or real-time data
- Provide links to next/previous pages in the response
// Example pagination metadata in response
{
"data": [...],
"pagination": {
"total": 243,
"limit": 20,
"offset": 40,
"hasMore": true
},
"links": {
"self": "/products?limit=20&offset=40",
"first": "/products?limit=20&offset=0",
"prev": "/products?limit=20&offset=20",
"next": "/products?limit=20&offset=60",
"last": "/products?limit=20&offset=240"
}
}
Field Selection
Field selection allows clients to request only the fields they need, reducing response size and improving performance.
// Request specific fields
GET /users?fields=id,name,email // Only return id, name, and email fields
GET /products?fields=id,name,price // Only return id, name, and price fields
// Include related resources
GET /orders?include=customer,items // Include related customer and items data
GET /products?include=category,reviews // Include related category and reviews data
Field Selection Best Practices
- Use a consistent parameter name (e.g., 'fields' or 'select')
- Support comma-separated lists of field names
- Allow inclusion of related resources with a separate parameter
- Consider authorization implications when allowing field selection
- Document available fields for each resource
Combined Query Parameters Example
GET /products
?category=electronics
&price_gte=100
&price_lte=500
&sort=price_asc
&fields=id,name,price,rating
&page=2
&perPage=20
This request:
- Filters for electronics products between $100 and $500
- Sorts by price in ascending order
- Returns only the id, name, price, and rating fields
- Requests the second page with 20 items per page
Special Operations and Action Endpoints
While RESTful APIs primarily use resource-based endpoints, some operations don't fit neatly into the CRUD model. Special endpoints can handle these cases while still maintaining a consistent API design.
Action Endpoints
Action endpoints represent operations or state transitions that don't map cleanly to CRUD operations.
// Action endpoints examples
POST /users/123/activate // Activate a user
POST /orders/456/cancel // Cancel an order
POST /emails/send // Send an email
POST /password-reset // Initiate a password reset
POST /checkout/complete // Complete a checkout process
Action Endpoint Best Practices
- Use nouns for resources and verbs for actions
- Use POST for actions (not GET, which should be safe and idempotent)
- Apply actions to specific resources where possible:
/orders/123/cancelnot/cancelOrder/123 - Keep action names clear and specific
- Consider if the action can be modeled as a resource state change (PATCH) before creating an action endpoint
Controller Resources
Controller resources represent operational concepts rather than data entities. They're useful for complex operations that involve multiple resources or don't naturally map to a single resource.
// Controller resource examples
POST /search // Complex search operation
POST /calculate-shipping // Calculate shipping costs
POST /validate-address // Validate a postal address
POST /generate-report // Generate a custom report
Controller Resource Best Practices
- Use sparingly - prefer resource-based endpoints when possible
- Use clear, descriptive names for the operation
- Implement as few methods as possible (usually just POST)
- Document inputs and outputs clearly
- Consider if the operation can be modeled as a standard resource before creating a controller
Bulk Operations
Bulk operations allow clients to perform the same operation on multiple resources in a single request.
// Bulk operation examples
POST /users/bulk-create // Create multiple users
PATCH /products/bulk-update // Update multiple products
DELETE /orders/bulk-delete // Delete multiple orders
// Alternative approach using standard endpoints with arrays
POST /users // Create multiple users
{
"users": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" }
]
}
Bulk Operation Best Practices
- Use consistent naming patterns like 'bulk-create', 'bulk-update'
- Accept arrays of resources in the request body
- Return appropriate status codes (e.g., 200 vs. 207 Multi-Status)
- Provide detailed success/failure information for each item in the batch
- Consider transaction semantics (all-or-nothing vs. partial success)
- Set reasonable limits on batch size
// Example response for a bulk operation
{
"status": "partial",
"results": [
{ "id": "user1", "status": "success", "data": { "id": 123, "name": "Alice" } },
{ "id": "user2", "status": "error", "message": "Email already exists" }
],
"summary": {
"total": 2,
"successful": 1,
"failed": 1
}
}
Special Operations Examples: E-commerce API
// Order processing actions
POST /orders/123/pay // Process payment for an order
POST /orders/123/ship // Mark an order as shipped
POST /orders/123/cancel // Cancel an order
POST /orders/123/refund // Process a refund for an order
// User account actions
POST /users/123/verify-email // Verify a user's email address
POST /users/123/change-password // Change a user's password
POST /users/123/suspend // Suspend a user account
POST /users/123/restore // Restore a suspended user account
// Shopping cart actions
POST /carts/456/add-item // Add an item to a shopping cart
POST /carts/456/remove-item // Remove an item from a shopping cart
POST /carts/456/clear // Clear all items from a cart
POST /carts/456/checkout // Process checkout for a cart
// Bulk operations
POST /products/bulk-import // Import multiple products
POST /inventory/bulk-update // Update inventory for multiple products
POST /orders/bulk-export // Export multiple orders
API Versioning Strategies
API versioning allows you to evolve your API over time without breaking existing clients. There are several approaches to versioning, each with its own benefits and drawbacks.
URL Path Versioning
Include the version in the URL path, often as a prefix.
// URL path versioning examples
GET /v1/users // Version 1 users endpoint
GET /v2/users // Version 2 users endpoint
GET /api/v1/products // Version 1 products endpoint
GET /api/v2/products // Version 2 products endpoint
Pros
- Very explicit and visible
- Easy to understand and implement
- Supports major version changes
- Works with any HTTP client
- Easy to test in a browser
Cons
Query Parameter Versioning
Include the version as a query parameter.
// Query parameter versioning examples
GET /users?version=1 // Version 1 users endpoint
GET /users?version=2 // Version 2 users endpoint
GET /products?api-version=2023-01-01 // Date-based version
Pros
- Resources maintain the same URL
- Easy to implement
- Works with caching (same URL path)
- Works with any HTTP client
Cons
Header Versioning
Include the version in an HTTP header.
// Custom header versioning
GET /users
X-API-Version: 1
// Accept header versioning
GET /users
Accept: application/vnd.myapi.v1+json
Pros
- Resources maintain the same URL
- More RESTful (uses content negotiation)
- Cleaner separation of concerns
- Works well with caching
Cons
Versioning Best Practices
- Start with a version, even if it's just v1
- Choose one versioning strategy and use it consistently
- Document your versioning strategy clearly
- Make breaking changes only in new versions
- Avoid unnecessary versioning for backward-compatible changes
- Support older versions for a reasonable period
- Communicate deprecation timelines in advance
- Consider semantic versioning (major.minor.patch) for your API
Implementing Versioning in Express
// URL path versioning in Express
const express = require('express');
const app = express();
// v1 routes
const v1UserRouter = require('./routes/v1/users');
app.use('/v1/users', v1UserRouter);
// v2 routes
const v2UserRouter = require('./routes/v2/users');
app.use('/v2/users', v2UserRouter);
// Query parameter versioning in Express
app.get('/users', (req, res) => {
const version = req.query.version || '1'; // Default to v1
if (version === '1') {
return handleV1UsersRequest(req, res);
} else if (version === '2') {
return handleV2UsersRequest(req, res);
} else {
return res.status(400).json({ error: 'Unsupported API version' });
}
});
// Header versioning in Express
app.get('/users', (req, res) => {
const version = req.header('X-API-Version') || '1'; // Default to v1
if (version === '1') {
return handleV1UsersRequest(req, res);
} else if (version === '2') {
return handleV2UsersRequest(req, res);
} else {
return res.status(400).json({ error: 'Unsupported API version' });
}
});
API Endpoint Documentation
Clear, comprehensive documentation is essential for API usability. Document all endpoints, parameters, request/response formats, and example usage.
What to Document
- Endpoint URL with HTTP method
- Description of what the endpoint does
- URL parameters with data types and constraints
- Query parameters with data types, defaults, and examples
- Request body schema (for POST, PUT, PATCH)
- Response format with example responses
- Status codes with descriptions of when each is returned
- Authentication requirements
- Rate limits or usage restrictions
- Example requests with corresponding responses
OpenAPI/Swagger Documentation
OpenAPI (formerly Swagger) is a specification for machine-readable API documentation. It allows you to describe your API in a standardized way that can be used to generate interactive documentation, client libraries, and server stubs.
// Example of OpenAPI documentation in Express
const express = require('express');
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const app = express();
// Swagger setup
const swaggerOptions = {
definition: {
openapi: '3.0.0',
info: {
title: 'Product API',
version: '1.0.0',
description: 'A simple product API'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
]
},
apis: ['./routes/*.js']
};
const swaggerDocs = swaggerJsdoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
// Example of documenting an endpoint using JSDoc annotations
/**
* @swagger
* /api/products:
* get:
* summary: Returns a list of products
* description: Retrieve a list of products with optional filtering
* parameters:
* - in: query
* name: category
* schema:
* type: string
* description: Filter by product category
* - in: query
* name: minPrice
* schema:
* type: number
* description: Minimum price filter
* responses:
* 200:
* description: A list of products
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* example: 1
* name:
* type: string
* example: "Wireless Headphones"
* price:
* type: number
* example: 99.99
*/
app.get('/api/products', (req, res) => {
// Implementation...
});
Documentation Best Practices
- Keep documentation in sync with code - consider documentation-as-code approaches
- Use auto-generation tools like Swagger when possible
- Provide realistic examples that users can copy and modify
- Document error responses as thoroughly as success responses
- Include authentication details with example tokens
- Test your documentation to ensure examples work
- Provide SDKs or client libraries for common languages
- Create getting started guides for common use cases
Best Practices for API Endpoint Design
Follow these best practices to create intuitive, consistent, and maintainable API endpoints:
Be Consistent
Establish clear naming conventions, URL patterns, and parameter formats, then apply them consistently across all endpoints.
Keep URLs Intuitive
Design URLs that are easy to read, understand, and remember. URLs should be self-descriptive and reflect the resource hierarchy.
Use HTTPS
Always use HTTPS for API endpoints to ensure data security and privacy.
Version Your API
Implement versioning from the start to allow your API to evolve without breaking existing clients.
Use Plural Resource Names
Use plural nouns for resource collections (e.g., /users, /products) for clearer distinction between collections and items.
Use Proper HTTP Methods
Map HTTP methods (GET, POST, PUT, PATCH, DELETE) correctly to CRUD operations.
Return Appropriate Status Codes
Use standard HTTP status codes to indicate the success or failure of requests.
Implement Pagination
Always implement pagination for collection endpoints to manage large result sets.
Support Filtering and Sorting
Allow clients to filter and sort collection results via query parameters.
Limit Nesting Depth
Keep URL nesting to 2-3 levels at most to maintain readability and usability.
Use Query Parameters for Filtering
Use query parameters, not URL path segments, for filtering, sorting, and pagination.
Accept and Return JSON
Use JSON as the default data format for requests and responses.
Provide Hypermedia Links
Include links to related resources and actions in responses to improve API discoverability.
Document Everything
Provide comprehensive documentation for all endpoints, parameters, and response formats.
Follow Security Best Practices
Implement proper authentication, authorization, and input validation for all endpoints.
Practical Exercise
Exercise: Design an API for a Blog Platform
In this exercise, you'll design a RESTful API for a blog platform with users, posts, comments, categories, and tags.
Requirements
- Users can register, login, and manage their profiles
- Users can create, edit, and delete their own posts
- Posts can have multiple categories and tags
- Users can comment on posts
- Users can like posts and comments
- Admins can manage all content
- The API should support filtering, sorting, and pagination
- The API should be versioned
Task 1: Define Resources
Identify the main resources in the system and their relationships:
- Users
- Posts
- Comments
- Categories
- Tags
- Likes
Task 2: Design Endpoints
Design endpoints for each resource, considering:
- Standard CRUD operations
- Nested resources
- Query parameters for filtering, sorting, and pagination
- Special operations and action endpoints
Sample Solution
Here's a sample API endpoint design for the blog platform:
// API Base URL: /api/v1
// Authentication and User Management
POST /auth/register // Register a new user
POST /auth/login // Login and get a token
POST /auth/logout // Logout
POST /auth/password-reset // Request password reset
POST /auth/password-reset/confirm // Confirm password reset
// Users
GET /users // List users (with filtering, sorting, pagination)
GET /users/{userId} // Get a specific user
GET /users/{userId}/posts // Get posts by a specific user
PUT /users/{userId} // Update a user (for admins or self)
PATCH /users/{userId} // Partially update a user (for admins or self)
DELETE /users/{userId} // Delete a user (for admins or self)
// User Actions
POST /users/{userId}/follow // Follow a user
DELETE /users/{userId}/follow // Unfollow a user
GET /users/{userId}/followers // Get followers of a user
GET /users/{userId}/following // Get users followed by a user
// Posts
GET /posts // List posts (with filtering, sorting, pagination)
POST /posts // Create a new post
GET /posts/{postId} // Get a specific post
PUT /posts/{postId} // Update a post (for author or admin)
PATCH /posts/{postId} // Partially update a post (for author or admin)
DELETE /posts/{postId} // Delete a post (for author or admin)
// Post Actions
POST /posts/{postId}/like // Like a post
DELETE /posts/{postId}/like // Unlike a post
POST /posts/{postId}/publish // Publish a draft post
POST /posts/{postId}/unpublish // Unpublish a post
// Post Relationships
GET /posts/{postId}/comments // Get comments for a post
POST /posts/{postId}/comments // Comment on a post
GET /posts/{postId}/likes // Get likes for a post
GET /posts/{postId}/related // Get related posts
// Comments
GET /comments // List comments (with filtering, sorting, pagination)
GET /comments/{commentId} // Get a specific comment
PUT /comments/{commentId} // Update a comment (for author or admin)
PATCH /comments/{commentId} // Partially update a comment (for author or admin)
DELETE /comments/{commentId} // Delete a comment (for author or admin)
// Comment Actions
POST /comments/{commentId}/like // Like a comment
DELETE /comments/{commentId}/like // Unlike a comment
// Categories
GET /categories // List categories
POST /categories // Create a category (admin only)
GET /categories/{categoryId} // Get a specific category
PUT /categories/{categoryId} // Update a category (admin only)
DELETE /categories/{categoryId} // Delete a category (admin only)
GET /categories/{categoryId}/posts // Get posts in a category
// Tags
GET /tags // List tags
POST /tags // Create a tag (admin only)
GET /tags/{tagId} // Get a specific tag
PUT /tags/{tagId} // Update a tag (admin only)
DELETE /tags/{tagId} // Delete a tag (admin only)
GET /tags/{tagId}/posts // Get posts with a tag
// Search
GET /search // Search across all content
POST /search // Advanced search with complex criteria
// Examples of query parameters
GET /posts?author=123 // Filter by author
GET /posts?category=technology // Filter by category
GET /posts?tags=javascript,node // Filter by multiple tags
GET /posts?status=published // Filter by status
GET /posts?createdAt_gte=2025-01-01 // Filter by creation date
GET /posts?sort=createdAt_desc // Sort by creation date descending
GET /posts?page=2&perPage=10 // Pagination
GET /posts?fields=id,title,author // Field selection
Task 3: Design Data Formats
Define the request and response formats for key endpoints:
// Example: Creating a new post
POST /api/v1/posts
{
"title": "Introduction to API Design",
"content": "This is the body of the post...",
"summary": "A brief overview of API design principles",
"categoryIds": [2, 5],
"tagIds": [7, 12, 15],
"status": "draft"
}
// Response
201 Created
{
"id": 123,
"title": "Introduction to API Design",
"content": "This is the body of the post...",
"summary": "A brief overview of API design principles",
"author": {
"id": 456,
"name": "John Doe",
"username": "johndoe"
},
"categories": [
{ "id": 2, "name": "Technology" },
{ "id": 5, "name": "Web Development" }
],
"tags": [
{ "id": 7, "name": "API" },
{ "id": 12, "name": "REST" },
{ "id": 15, "name": "Design" }
],
"status": "draft",
"createdAt": "2025-05-04T12:34:56Z",
"updatedAt": "2025-05-04T12:34:56Z",
"_links": {
"self": { "href": "/api/v1/posts/123" },
"author": { "href": "/api/v1/users/456" },
"comments": { "href": "/api/v1/posts/123/comments" },
"publish": { "href": "/api/v1/posts/123/publish", "method": "POST" }
}
}
Task 4: Documentation
Create documentation for one of the endpoints using OpenAPI/Swagger format.
/**
* @swagger
* /api/v1/posts:
* get:
* summary: Returns a list of blog posts
* description: Retrieve a list of blog posts with optional filtering, sorting, and pagination
* parameters:
* - in: query
* name: author
* schema:
* type: integer
* description: Filter by author ID
* - in: query
* name: category
* schema:
* type: integer
* description: Filter by category ID
* - in: query
* name: tags
* schema:
* type: string
* description: Comma-separated list of tag IDs
* - in: query
* name: status
* schema:
* type: string
* enum: [published, draft]
* description: Filter by post status
* - in: query
* name: sort
* schema:
* type: string
* default: createdAt_desc
* description: Sort order (createdAt_asc, createdAt_desc, title_asc, title_desc)
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: Page number
* - in: query
* name: perPage
* schema:
* type: integer
* default: 10
* description: Number of items per page
* responses:
* 200:
* description: A list of blog posts
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* example: 123
* title:
* type: string
* example: "Introduction to API Design"
* summary:
* type: string
* example: "A brief overview of API design principles"
* author:
* type: object
* properties:
* id:
* type: integer
* example: 456
* name:
* type: string
* example: "John Doe"
* status:
* type: string
* example: "published"
* createdAt:
* type: string
* format: date-time
* example: "2025-05-04T12:34:56Z"
* pagination:
* type: object
* properties:
* total:
* type: integer
* example: 50
* page:
* type: integer
* example: 1
* perPage:
* type: integer
* example: 10
* pages:
* type: integer
* example: 5
* links:
* type: object
* properties:
* self:
* type: string
* example: "/api/v1/posts?page=1&perPage=10"
* next:
* type: string
* example: "/api/v1/posts?page=2&perPage=10"
* last:
* type: string
* example: "/api/v1/posts?page=5&perPage=10"
*/
Challenge Extensions
- Add support for field selection to allow clients to request specific fields
- Design a rate limiting scheme for the API
- Design a caching strategy for read-heavy endpoints
- Implement HATEOAS links for all resources
- Design endpoints for analytics and reporting
Summary
- Resource Naming: Use plural nouns, be consistent, and follow clear naming conventions
- URL Structure: Design intuitive hierarchies, limit nesting depth, and use descriptive path parameters
- Query Parameters: Implement filtering, sorting, pagination, and field selection through query parameters
- Special Operations: Design action endpoints and controller resources for operations that don't fit the CRUD model
- Versioning: Choose a versioning strategy and apply it consistently to allow API evolution
- Documentation: Provide comprehensive, accurate documentation for all endpoints
- Well-designed API endpoints are intuitive, consistent, predictable, and maintainable
Further Reading
Next Lesson Preview
In our next session, we'll explore Working with Data in Node.js applications. We'll learn how to handle various data formats, connect to databases, perform CRUD operations, and implement data validation. You'll understand different approaches for working with both SQL and NoSQL databases, and how to design efficient data models for your applications.