- Express.js Basics
- Express.js HOME
- Express.js Introduction
- Express.js Installation
- Express.js Basic App
- Express.js Routing
- Basics Routing
- Route Parameters
- Handling Query Strings
- Router Middleware
- Middleware
- What is Middleware?
- Application-Level Middleware
- Router-Level Middleware
- Built-In Middleware
- Error-Handling Middleware
- Third-Party Middleware
- Express.js HTTP
- Handling GET Requests
- Handling POST Requests
- Handling PUT Requests
- Handling DELETE Requests
- Templating Engines
- Using Templating Engines
- Setting Up EJS
- Setting Up Handlebars
- Setting Up Pug
- Request/Response
- Request Object
- Response Object
- Handling JSON Data
- Handling Form Data
- Static Files
- Serving Static Files
- Setting Up Static Folders
- Managing Assets
- Express.js Advanced
- Middleware Stack
- CORS in Express.js
- JWT Authentication
- Session Handling
- File Uploads
- Error Handling
- Databases
- Express.js with MongoDB
- MongoDB CRUD Operations
- Express.js with MySQL
- MySQL CRUD Operations
- Deployment
- Deploying Express.js Apps to Heroku
- Deploying Express.js Apps to AWS
- Deploying Express.js Apps to Vercel
Express.js What is Middleware
Middleware in Express.js is a fundamental concept that acts as the "glue" between the incoming request and the final response. Think of it as an assembly line: when a request comes in, it passes through several stations (middleware functions). Each station can inspect the request, modify it, perform an action (like logging or checking permissions), or even stop the process entirely and send back an error.
Technically, a middleware function has access to three key components: the request object (req), the response object (res), and the next() middleware function. This next() function is what triggers the next piece of logic in the stack.
Key Features of Middleware
- Request Handling: Middleware can perform tasks like parsing JSON data, cookies, or headers before your main logic even sees the request.
- Sequence Execution: Express executes middleware in the exact order you define them in your code. This allows you to create a controlled pipeline.
- Reusable: Instead of writing authentication logic for every single route, you can write it once as a middleware and apply it wherever needed.
- Error Handling: Special middleware can catch bugs or broken processes and return a clean, user-friendly error message instead of crashing the server.
Types of Middleware
Application-Level Middleware:
These are bound to your main app instance using app.use() or app.METHOD(). They run for every request that enters your application (unless restricted to a specific path).
app.use((req, res, next) => {
console.log(`Time: ${Date.now()} | Method: ${req.method} | URL: ${req.url}`);
next(); // Essential to pass control to the next function
});
next(). If you don't call next() and you don't send a response (like res.send()), your request will hang indefinitely, and the browser will eventually time out.
Router-Level Middleware:
This works exactly like application-level middleware but is bound to an instance of express.Router(). This is perfect for grouping related routes (like all /api/v1/user routes) and applying logic only to that group.
const router = express.Router();
// This only runs for routes handled by this specific router
router.use((req, res, next) => {
console.log('User-specific request detected');
next();
});
router.get('/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
Built-in Middleware:
Express used to rely heavily on external libraries, but it now includes essential built-in middleware. The most common are express.json() for parsing JSON bodies and express.static() for serving images, CSS, or HTML files:
// Allows your app to read JSON data sent in a POST request
app.use(express.json());
// Makes the 'public' folder accessible to the world (for CSS, JS, Images)
app.use(express.static('public'));
Error-Handling Middleware:
Unlike other middleware, error-handling functions always take four arguments instead of three: (err, req, res, next). This signature tells Express that this is an error handler.
app.use((err, req, res, next) => {
console.error(err.stack); // Log the technical error for the dev
res.status(500).send('Internal Server Error: Our team is looking into it!');
});
app.use() and route calls; otherwise, it won't catch the errors thrown by them.
Third-Party Middleware:
The Node ecosystem has thousands of middleware packages. Common examples include cors (to allow cross-origin requests), morgan (for HTTP request logging), and helmet (for securing HTTP headers):
const cors = require('cors');
const morgan = require('morgan');
app.use(cors()); // Allow requests from different domains
app.use(morgan('dev')); // Log detailed request info to the console
Steps to Implement Middleware in Express.js
Set up a Basic Express Application
Start by creating a directory, initializing npm, and installing the Express package:
mkdir my-express-app
cd my-express-app
npm init -y
npm install express --save
Define Middleware Functions
In your app.js file, you can create custom logic to process requests. Here is a real-world example of a "Request Logger" and a "Secret Header" checker:
const express = require('express');
const app = express();
// 1. Custom Middleware: Simple Logger
app.use((req, res, next) => {
console.log(`${req.method} request to ${req.url}`);
next();
});
// 2. Custom Middleware: Protection (Example)
const checkAuth = (req, res, next) => {
const password = req.headers['x-secret-key'];
if (password === 'my-super-secret') {
next(); // Authorization successful
} else {
res.status(403).send('Forbidden: Invalid Secret Key');
}
};
// Route handler (not protected)
app.get('/', (req, res) => {
res.send('Welcome to the Homepage!');
});
// Route handler (protected by checkAuth middleware)
app.get('/admin', checkAuth, (req, res) => {
res.send('Welcome to the Admin Dashboard!');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
Test Middleware Functionality
Run your app with node app.js. You will notice that:
- Every time you visit a page, the console logs the method and URL.
- Visiting
/adminwithout the correct header will result in a 403 Forbidden error. - This demonstrates how middleware can filter requests before they reach your logic.
Use Middleware for Error Handling
To prevent your app from crashing if something unexpected happens, always include a catch-all error handler at the very bottom of your file:
app.get('/broken', (req, res) => {
throw new Error('This is a manual crash!');
});
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
Middleware Flow
- Request enters the server: The browser sends a request to your Express server.
- The Middleware Pipeline: Express passes the request through your middleware functions one by one, in the order they were defined.
- Next() or End: Each function must either call
next()to hand off the request or terminate the cycle by sending a response (e.g.,res.json()). - Final Destination: If everything passes, the request reaches your route handler (like
app.get), which sends the final data back to the client.
Summary
Middleware is the backbone of Express.js development. It provides a modular way to handle common web development tasks—like authentication, data parsing, and logging—without cluttering your main route logic. By mastering the request-response cycle and the use of the next() function, you can build scalable, secure, and well-organized Node.js applications.
express.json() and express.urlencoded() early in your application stack to ensure your route handlers can properly access request body data.