- 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 Application-Level Middleware
In the world of Express.js, middleware is the backbone of your application. Application-level middleware refers to functions that are bound directly to an instance of the app object (created by calling express()). Think of it as a series of "checkpoints" or a processing pipeline that every incoming request travels through before reaching your final route handler.
By using app.use() or app.METHOD() (where METHOD is the HTTP verb like GET or POST), you can intercept requests to perform logic like logging user activity, parsing incoming data, or checking if a user is logged in.
Key Features of Application-Level Middleware
- Global or Route-specific: You can choose to run middleware for every single request hitting your server (global) or restrict it to specific URL paths.
- Request Processing: Middleware has full access to the Request (
req) and Response (res) objects. This allows you to attach custom data toreqthat your later routes can use. - The Next Function: This is the third argument, usually named
next. When called, it passes control to the next middleware function in the stack. If you don't call it, the request will hang. - Sequential Execution: Order is everything. Express executes middleware in the exact order you write them in your code.
res.send()), it must call next(). Otherwise, the browser will just keep spinning, and the request will eventually time out.
Steps to Implement Application-Level Middleware
Set up a Basic Express Application
To get started, you need to initialize your Node.js environment and install the Express package:
npm init -y
npm install express --save
Define Application-Level Middleware
Global middleware is defined using app.use() without a path. This ensures it runs regardless of whether the user visits /home, /contact, or /api.
const express = require('express');
const app = express();
// Global Application-level middleware
app.use((req, res, next) => {
console.log(`Time: ${Date.now()} - Request Type: ${req.method}`);
// We call next() to move to the next function in line
next();
});
// A simple route handler
app.get('/', (req, res) => {
res.send('Welcome to the Homepage!');
});
// Another route handler
app.get('/about', (req, res) => {
res.send('This is the About page.');
});
app.listen(3000, () => {
console.log('Server is live at http://localhost:3000');
});
app.get, app.post, etc.).
Test Application-Level Middleware
Start your server by running node app.js. Every time you refresh your browser at http://localhost:3000 or http://localhost:3000/about, you will see the timestamp and request method logged in your terminal. This confirms that the middleware is successfully intercepting every request.
Use Middleware for Specific Routes
Sometimes you only want middleware to run on specific paths. For example, you might want to log activity only for the /admin section of your site:
app.use('/admin', (req, res, next) => {
console.log('An admin route was accessed!');
next();
});
app.use('/admin', ...) matches any path that starts with /admin. This means it will trigger for /admin/settings, /admin/users, and so on.
Error-Handling Middleware
Express handles errors using a special type of middleware that takes four arguments instead of three. This should always be defined last, after all other app.use() and route calls.
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke on our end!');
});
Example of Logging and Authentication Middleware
In real-world applications, middleware is most commonly used for logging and security. Here are two practical examples:
Example 1: Request Logger:
Instead of just logging a string, let's log detailed information that could help with debugging production issues.
app.use((req, res, next) => {
const userAgent = req.get('User-Agent');
console.log(`[LOG] ${req.method} ${req.url} - Agent: ${userAgent}`);
next();
});
Example 2: Authentication Guard:
You can create a function to protect routes. If a condition isn't met (e.g., the user isn't logged in), you can stop the request immediately by sending a response.
// Mock authentication function
const checkAuth = (req, res, next) => {
const isAdmin = req.query.admin === 'true'; // A simple check for demo purposes
if (isAdmin) {
next(); // User is admin, proceed to the route
} else {
res.status(403).send('Access Denied: You do not have permission to view this page.');
}
};
// Only the /dashboard route is protected by this middleware
app.get('/dashboard', checkAuth, (req, res) => {
res.send('Welcome to the Secret Dashboard!');
});
app.get('/path', middleware1, middleware2, (req, res) => { ... }).
Summary
Application-level middleware is an incredibly powerful tool in the Express.js ecosystem. By using app.use(), you create a modular, readable, and maintainable codebase. Whether you are building a simple logger, a complex authentication system, or handling global errors, middleware allows you to separate these concerns from your main business logic. Remember: the order of definition matters, and always call next() unless you are intentionally ending the request.