Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Express.js Middleware

Middleware in Express.js are functions that execute during the lifecycle of a request to the server. They can modify the request and response objects, end the request-response cycle, and call the next middleware function. This guide covers key concepts, types of middleware, examples, and best practices for using middleware in Express.js.

Key Concepts of Express.js Middleware

  • Middleware Function: A function that has access to the request and response objects, and the next middleware function in the application’s request-response cycle.
  • next Function: A function that, when called, executes the next middleware function in the stack.
  • Application-Level Middleware: Middleware bound to an instance of the app object using app.use() or app.METHOD().
  • Router-Level Middleware: Middleware bound to an instance of the express.Router() class.
  • Error-Handling Middleware: Middleware that takes four arguments and is used for handling errors.
  • Built-In Middleware: Middleware included with Express.js, such as express.static, express.json, and express.urlencoded.
  • Third-Party Middleware: Middleware provided by third-party libraries, available via npm.

Creating and Using Middleware

Create middleware functions and use them in your application with app.use() or app.METHOD():

Example: Basic Middleware

// basic-middleware.js
const express = require('express');
const app = express();
const port = 3000;

// Middleware function
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next(); // Call the next middleware function
};

// Use middleware
app.use(logger);

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

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Application-Level Middleware

Bind middleware to an instance of the app object:

Example: Application-Level Middleware

// app-level-middleware.js
const express = require('express');
const app = express();
const port = 3000;

// Middleware functions
const requestTime = (req, res, next) => {
    req.requestTime = Date.now();
    next();
};

const logger = (req, res, next) => {
    console.log(`[${new Date(req.requestTime).toISOString()}] ${req.method} ${req.url}`);
    next();
};

// Use middleware
app.use(requestTime);
app.use(logger);

app.get('/', (req, res) => {
    res.send(`Hello, World! Request received at ${req.requestTime}`);
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Router-Level Middleware

Bind middleware to an instance of the express.Router() class:

Example: Router-Level Middleware

// router-middleware.js
const express = require('express');
const app = express();
const router = express.Router();
const port = 3000;

// Middleware function
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
};

// Use middleware in the router
router.use(logger);

router.get('/', (req, res) => {
    res.send('Home Page');
});

router.get('/about', (req, res) => {
    res.send('About Page');
});

// Use the router
app.use('/', router);

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Error-Handling Middleware

Error-handling middleware takes four arguments: (err, req, res, next). Define error-handling middleware last, after other app.use() and routes calls:

Example: Error-Handling Middleware

// error-handling.js
const express = require('express');
const app = express();
const port = 3000;

// Middleware function to simulate an error
const simulateError = (req, res, next) => {
    const err = new Error('Something went wrong!');
    err.status = 500;
    next(err); // Pass the error to the next middleware
};

// Error-handling middleware
const errorHandler = (err, req, res, next) => {
    console.error(err.stack);
    res.status(err.status || 500);
    res.send(`Error: ${err.message}`);
};

// Use middleware
app.use(simulateError);
app.use(errorHandler);

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Built-In Middleware

Express.js includes built-in middleware functions, such as express.static, express.json, and express.urlencoded:

Example: Using Built-In Middleware

// built-in-middleware.js
const express = require('express');
const path = require('path');
const app = express();
const port = 3000;

// Use built-in middleware
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post('/submit', (req, res) => {
    res.send(`Form Submitted: ${req.body.name}`);
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Third-Party Middleware

Use third-party middleware available via npm, such as morgan for logging, body-parser for parsing request bodies, and cors for enabling Cross-Origin Resource Sharing:

Example: Using Third-Party Middleware

// third-party-middleware.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 3000;

// Use third-party middleware
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(cors());

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

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Best Practices for Using Middleware

  • Use Middleware Functions: Leverage middleware for common tasks such as logging, authentication, error handling, and data parsing.
  • Order Matters: The order in which middleware is defined matters, as it affects the request-response cycle.
  • Modularize Middleware: Organize middleware functions into separate modules for better maintainability and readability.
  • Handle Errors Properly: Implement error-handling middleware to manage runtime exceptions and send appropriate responses.
  • Security: Use security-related middleware to protect your application from common vulnerabilities, such as helmet for setting HTTP headers.

Testing Middleware

Test your middleware functions using frameworks like Mocha, Chai, and Supertest:

Example: Testing Middleware with Mocha, Chai, and Supertest

// Install Mocha, Chai, and Supertest
// npm install --save-dev mocha chai supertest

// test/middleware.test.js
const chai = require('chai');
const expect = chai.expect;
const request = require('supertest');
const express = require('express');

const app = express();

// Middleware function
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
};

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

describe('GET /', () => {
    it('should respond with "Hello, World!"', (done) => {
        request(app)
            .get('/')
            .expect('Content-Type', /text\/html/)
            .expect(200, 'Hello, World!', done);
    });
});

// Define test script in package.json
// "scripts": {
//   "test": "mocha"
// }

// Run tests with NPM
// npm run test

Key Points

  • Middleware Function: A function that has access to the request and response objects, and the next middleware function in the application’s request-response cycle.
  • next Function: A function that, when called, executes the next middleware function in the stack.
  • Application-Level Middleware: Middleware bound to an instance of the app object using app.use() or app.METHOD().
  • Follow best practices for using middleware, such as leveraging middleware for common tasks, organizing middleware into separate modules, handling errors properly, and using security-related middleware to protect your application.

Conclusion

Middleware in Express.js are functions that execute during the lifecycle of a request to the server. By understanding and implementing the key concepts, types of middleware, examples, and best practices covered in this guide, you can effectively use middleware in your Express.js applications. Happy coding!