Home > Web Front-end > JS Tutorial > Mastering Express.js: A Deep Dive

Mastering Express.js: A Deep Dive

Susan Sarandon
Release: 2025-01-05 06:46:40
Original
381 people have browsed it

Mastering Express.js: A Deep Dive

Express is an extremely commonly used web server application framework in Node.js. Essentially, a framework is a code structure that adheres to specific rules and has two key characteristics:

  • It encapsulates APIs, enabling developers to concentrate more on writing business code.
  • It has established processes and standard specifications.

The core features of the Express framework are as follows:

  • It can configure middleware to respond to various HTTP requests.
  • It defines a route table for executing different types of HTTP request actions.
  • It supports passing parameters to templates to achieve dynamic rendering of HTML pages.

This article will analyze how Express implements middleware registration, the next mechanism, and route handling by implementing a simple LikeExpress class.

Express Analysis

Let's first explore the functions it provides through two Express code examples:

Express Official Website Hello World Example

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

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});
Copy after login
Copy after login
Copy after login

Analysis of the Entry File app.js

The following is the code of the entry file app.js of the Express project generated by the express-generator scaffolding:

// Handle errors caused by unmatched routes
const createError = require('http-errors');
const express = require('express');
const path = require('path');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

// `app` is an Express instance
const app = express();

// View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// Parse JSON format data in post requests and add the `body` field to the `req` object
app.use(express.json());
// Parse the urlencoded format data in post requests and add the `body` field to the `req` object
app.use(express.urlencoded({ extended: false }));

// Static file handling
app.use(express.static(path.join(__dirname, 'public')));

// Register top-level routes
app.use('/', indexRouter);
app.use('/users', usersRouter);

// Catch 404 errors and forward them to the error handler
app.use((req, res, next) => {
    next(createError(404));
});

// Error handling
app.use((err, req, res, next) => {
    // Set local variables to display error messages in the development environment
    res.locals.message = err.message;
    // Decide whether to display the full error according to the environment variable. Display in development, hide in production.
    res.locals.error = req.app.get('env') === 'development'? err : {};
    // Render the error page
    res.status(err.status || 500);
    res.render('error');
});

module.exports = app;
Copy after login
Copy after login
Copy after login

From the above two code segments, we can see that the Express instance app mainly has three core methods:

  1. app.use([path,] callback [, callback...]): Used to register middleware. When the request path matches the set rules, the corresponding middleware function will be executed.
    • path: Specifies the path for invoking the middleware function.
    • callback: The callback function can take various forms. It can be a single middleware function, a series of middleware functions separated by commas, an array of middleware functions, or a combination of all the above.
  2. app.get() and app.post(): These methods are similar to use(), also for registering middleware. However, they are bound to HTTP request methods. Only when the corresponding HTTP request method is used will the registration of the relevant middleware be triggered.
  3. app.listen(): Responsible for creating an httpServer and passing the parameters required by server.listen().

Code Implementation

Based on the analysis of the functions of the Express code, we know that the implementation of Express focuses on three points:

  • The registration process of middleware functions.
  • The core next mechanism in the middleware functions.
  • Route handling, with a focus on path matching.

Based on these points, we will implement a simple LikeExpress class below.

1. Basic Structure of the Class

First, clarify the main methods that this class needs to implement:

  • use(): Implements general middleware registration.
  • get() and post(): Implement middleware registration related to HTTP requests.
  • listen(): Essentially, it is the listen() function of httpServer. In the listen() function of this class, an httpServer is created, parameters are passed through, requests are listened to, and the callback function (req, res) => {} is executed.

Review the usage of the native Node httpServer:

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

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});
Copy after login
Copy after login
Copy after login

Accordingly, the basic structure of the LikeExpress class is as follows:

// Handle errors caused by unmatched routes
const createError = require('http-errors');
const express = require('express');
const path = require('path');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

// `app` is an Express instance
const app = express();

// View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// Parse JSON format data in post requests and add the `body` field to the `req` object
app.use(express.json());
// Parse the urlencoded format data in post requests and add the `body` field to the `req` object
app.use(express.urlencoded({ extended: false }));

// Static file handling
app.use(express.static(path.join(__dirname, 'public')));

// Register top-level routes
app.use('/', indexRouter);
app.use('/users', usersRouter);

// Catch 404 errors and forward them to the error handler
app.use((req, res, next) => {
    next(createError(404));
});

// Error handling
app.use((err, req, res, next) => {
    // Set local variables to display error messages in the development environment
    res.locals.message = err.message;
    // Decide whether to display the full error according to the environment variable. Display in development, hide in production.
    res.locals.error = req.app.get('env') === 'development'? err : {};
    // Render the error page
    res.status(err.status || 500);
    res.render('error');
});

module.exports = app;
Copy after login
Copy after login
Copy after login

2. Middleware Registration

From app.use([path,] callback [, callback...]), we can see that middleware can be an array of functions or a single function. To simplify the implementation, we uniformly process the middleware as an array of functions. In the LikeExpress class, the three methods use(), get(), and post() can all implement middleware registration. Only the triggered middleware varies due to different request methods. So we consider:

  • Abstracting a general middleware registration function.
  • Creating arrays of middleware functions for these three methods to store the middleware corresponding to different requests. Since use() is a general middleware registration method for all requests, the array storing use() middleware is the union of the arrays for get() and post().

Middleware Queue Array

The middleware array needs to be placed in a public area for easy access by methods in the class. So, we put the middleware array in the constructor() constructor function.

const http = require("http");
const server = http.createServer((req, res) => {
    res.end("hello");
});
server.listen(3003, "127.0.0.1", () => {
    console.log("node service started successfully");
});
Copy after login
Copy after login

Middleware Registration Function

Middleware registration means storing the middleware in the corresponding middleware array. The middleware registration function needs to parse the incoming parameters. The first parameter may be a route or middleware, so it is necessary to first determine whether it is a route. If it is, output it as it is; otherwise, the default is the root route, and then convert the remaining middleware parameters into an array.

const http = require('http');

class LikeExpress {
    constructor() {}

    use() {}

    get() {}

    post() {}

    // httpServer callback function
    callback() {
        return (req, res) => {
            res.json = function (data) {
                res.setHeader('content-type', 'application/json');
                res.end(JSON.stringify(data));
            };
        };
    }

    listen(...args) {
        const server = http.createServer(this.callback());
        server.listen(...args);
    }
}

module.exports = () => {
    return new LikeExpress();
};
Copy after login
Copy after login

Implementation of use(), get(), and post()

With the general middleware registration function register(), it is easy to implement use(), get(), and post(), just store the middleware in the corresponding arrays.

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

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});
Copy after login
Copy after login
Copy after login

3. Route Matching Processing

When the first parameter of the registration function is a route, the corresponding middleware function will be triggered only when the request path matches the route or is its sub-route. So, we need a route matching function to extract the middleware array of the matching route according to the request method and request path for the subsequent callback() function to execute:

// Handle errors caused by unmatched routes
const createError = require('http-errors');
const express = require('express');
const path = require('path');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

// `app` is an Express instance
const app = express();

// View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// Parse JSON format data in post requests and add the `body` field to the `req` object
app.use(express.json());
// Parse the urlencoded format data in post requests and add the `body` field to the `req` object
app.use(express.urlencoded({ extended: false }));

// Static file handling
app.use(express.static(path.join(__dirname, 'public')));

// Register top-level routes
app.use('/', indexRouter);
app.use('/users', usersRouter);

// Catch 404 errors and forward them to the error handler
app.use((req, res, next) => {
    next(createError(404));
});

// Error handling
app.use((err, req, res, next) => {
    // Set local variables to display error messages in the development environment
    res.locals.message = err.message;
    // Decide whether to display the full error according to the environment variable. Display in development, hide in production.
    res.locals.error = req.app.get('env') === 'development'? err : {};
    // Render the error page
    res.status(err.status || 500);
    res.render('error');
});

module.exports = app;
Copy after login
Copy after login
Copy after login

Then, in the callback function callback() of the httpServer, extract the middleware that needs to be executed:

const http = require("http");
const server = http.createServer((req, res) => {
    res.end("hello");
});
server.listen(3003, "127.0.0.1", () => {
    console.log("node service started successfully");
});
Copy after login
Copy after login

4. Implementation of the next Mechanism

The parameters of the Express middleware function are req, res, and next, where next is a function. Only by calling it can the middleware functions be executed in sequence, similar to next() in ES6 Generator. In our implementation, we need to write a next() function with the following requirements:

  • Extract one middleware from the middleware queue array in order each time.
  • Pass the next() function into the extracted middleware. Because the middleware array is public, each time next() is executed, the first middleware function in the array will be taken out and executed, thus achieving the effect of sequential execution of middleware.
const http = require('http');

class LikeExpress {
    constructor() {}

    use() {}

    get() {}

    post() {}

    // httpServer callback function
    callback() {
        return (req, res) => {
            res.json = function (data) {
                res.setHeader('content-type', 'application/json');
                res.end(JSON.stringify(data));
            };
        };
    }

    listen(...args) {
        const server = http.createServer(this.callback());
        server.listen(...args);
    }
}

module.exports = () => {
    return new LikeExpress();
};
Copy after login
Copy after login

Express Code

constructor() {
    // List of stored middleware
    this.routes = {
        all: [], // General middleware
        get: [], // Middleware for get requests
        post: [], // Middleware for post requests
    };
}
Copy after login

Leapcell: The Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis

Mastering Express.js: A Deep Dive

Finally, let me introduce a platform that is very suitable for deploying Express: Leapcell.

Leapcell is a serverless platform with the following characteristics:

1. Multi-Language Support

  • Develop with JavaScript, Python, Go, or Rust.

2. Deploy unlimited projects for free

  • Pay only for usage — no requests, no charges.

3. Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

4. Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

5. Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the documentation!

Leapcell Twitter: https://x.com/LeapcellHQ

The above is the detailed content of Mastering Express.js: A Deep Dive. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template