API Endpoint Design

Creating intuitive, consistent, and maintainable API endpoints

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

flowchart TD A[API Endpoint Design] --> B[Resource Naming] A --> C[URL Structure] A --> D[Query Parameters] A --> E[Special Operations] A --> F[Versioning] B --> B1[Nouns, Not Verbs] B --> B2[Plural vs. Singular] B --> B3[Naming Conventions] C --> C1[Resource Hierarchy] C --> C2[Nested Resources] C --> C3[Path Parameters] D --> D1[Filtering] D --> D2[Sorting] D --> D3[Pagination] D --> D4[Field Selection] E --> E1[Actions] E --> E2[Controllers] E --> E3[Bulk Operations] F --> F1[URL Path Versioning] F --> F2[Query Parameter Versioning] F --> F3[Header Versioning]

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
  • More intuitive for collections and items
  • Consistent URL structure
  • Industry standard practice
  • Some words have irregular plurals
  • Slightly longer URLs
Singular /user
/user/123
  • Reflects the single resource type
  • Avoids issues with irregular plurals
  • Less intuitive distinction between collection and item
  • Less common in practice

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:

  • kebab-case (most common for URLs)
  • snake_case
  • camelCase (less common in URLs)
/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
graph TD A["/users"] --> B["/users/{userId}"] B --> C["/users/{userId}/posts"] C --> D["/users/{userId}/posts/{postId}"] D --> E["/users/{userId}/posts/{postId}/comments"] E --> F["/users/{userId}/posts/{postId}/comments/{commentId}"] G["/posts"] --> H["/posts/{postId}"] H --> I["/posts/{postId}/comments"] I --> J["/posts/{postId}/comments/{commentId}"] K["/comments"] --> L["/comments/{commentId}"]

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/cancel not /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

  • Resources have different URLs across versions
  • Less RESTful (version isn't a resource property)
  • Can lead to URL proliferation
  • Requires routing changes for version updates
  • 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

  • Less visible, easier to overlook
  • Clients might inadvertently use default version
  • Doesn't work well with URL-based caching strategies
  • Mixing concern of versioning with data filtering
  • 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

  • Less visible, harder to discover
  • More complex to implement
  • Harder to test without specialized tools
  • Not all proxies/caches preserve custom headers
  • 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

    1. Add support for field selection to allow clients to request specific fields
    2. Design a rate limiting scheme for the API
    3. Design a caching strategy for read-heavy endpoints
    4. Implement HATEOAS links for all resources
    5. Design endpoints for analytics and reporting

    Summary

    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.