Express.js API Development Guide

Express.js is a high-performance web application framework built on the Node.js platform, designed to simplify the process of building HTTP servers. Compared to the traditional native http module in Node.js, Express offers richer middleware mechanisms, robust routing management, and comprehensive error handling solutions, significantly enhancing development efficiency. Before understanding the Express API interfaces, one must master its core architecture: it is essentially a lightweight web framework that allows developers to handle requests and responses through "pipeline"-style middleware chains.

The core advantage of Express lies in the flexibility of its middleware system. Every middleware function receives three parameters: req (the HTTP request object), res (the HTTP response object), and the next() function. This design enables us to decompose logic into multiple independent modules, such as logging, authentication, or error handling; each module focuses on specific functional points without interfering with others. Furthermore, Express's built-in routing system supports RESTful API design specifications, allowing different business logic functions to be mapped via URL paths and HTTP methods (such as GET, POST).
The first step in building an Express API is initializing the project and defining a basic route structure. We need to install the express package as a core dependency, then create a main entry file to handle service startup and business logic registration. Below is a standard initial code example:

const express = require('express');
const app = express();

// Enable JSON parsing middleware to allow receiving request bodies in JSON format
app.use(express.json());

// GET /users - Retrieve all users list
app.get('/users', (req, res) => {
    // Simulate data source
    const users = [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' }
    ];
    
    // Set response headers and return JSON data
    res.status(200).json({ 
        success: true, 
        data: users, 
        count: users.length 
    });
});

// POST /users - Create a new user
app.post('/users', (req, res) => {
    const { name } = req.body; // Destructure request body
    
    if (!name || typeof name !== 'string') {
        return res.status(400).json({ 
            success: false, 
            message: 'Name field cannot be empty' 
        });
    }

    // Simulate creating a user and returning the new object's ID
    const newUser = { id: Date.now(), name };
    
    res.status(201).json({ 
        success: true, 
        data: newUser, 
        message: 'User created successfully',
        location: `/users/${newUser.id}` // Provide redirect location for subsequent delete operations
    });
});

// Start server listening on port 3000
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

In the example above, we demonstrate how to define static routes and handle different HTTP methods. The express.json() middleware is crucial as it automatically parses request bodies with Content-Type: application/json and converts them into JavaScript objects passed to the callback function. Additionally, note the usage of response status codes: 200 indicates successful retrieval of resources, while 201 signifies that a new resource has been successfully created and its URI (location) is returned; this is vital for RESTful API integrity.

Enhancing Functionality and Error Handling with Middleware

As business logic increases, simple route callbacks are no longer sufficient, making it necessary to introduce more advanced middleware strategies. Express supports chaining multiple middleware functions into a "pipeline," where each middleware has the opportunity to modify request or response objects, or terminate the flow under specific conditions by calling res.end(). Additionally, custom error handling middleware is a key component in building robust APIs.

We can create a universal validation middleware to uniformly check input data:

// Define a unified parameter validation middleware
function validateUser(req, res, next) {
    const { name } = req.body;
    
    if (!name || !/^[a-zA-Z]+$/.test(name)) {
        return next(new Error('Name must be a non-empty string of letters'));
    }
    next(); // Validation passed, proceed to execute the next middleware or route logic
}

// Define global error handling middleware (must be placed after all other middlewares)
app.use((err, req, res, next) => {
    console.error(err.stack);
    
    // Return different response structures based on error type
    if (err instanceof Error && err.message.includes('Parameter validation failed')) {
        return res.status(400).json({ 
            success: false, 
            message: 'Request data format is incorrect',
            details: err.message 
        });
    } else {
        // Return a generic hint for other unexpected errors to avoid leaking sensitive information
        return res.status(500).json({ 
            success: false, 
            message: 'Internal server error' 
        });
    }
});

// Mount the validation middleware before the route
app.post('/users', validateUser, (req, res) => {
    // ... original business logic
});

By encapsulating complex validation rules into independent functions in this manner, we improve code readability while ensuring uniformity and security in error handling. The global error handling middleware must be placed after all other middlewares in the application to capture any exceptions that are not intercepted elsewhere and return a friendly JSON response to frontend callers.