Middleware Implementation and Mechanics
Creating Middleware
Middleware files should be placed in the pages/api/middleware directory of your project. If the directory does not exist, you need to create it manually.
mkdir pages/api/middlewareWriting Middleware
A middleware file is a standard JavaScript file that accepts a handler function as a parameter and returns a new handler function.
// pages/api/middleware/log.js
export default function log(handler) {
return async function (req, res, next) {
console.log(`Handling request for ${req.url}`);
await handler(req, res, next);
};
}Using Middleware
To use middleware, import it into an API route file and apply it to the route handler function.
// pages/api/hello.js
import log from '../../../middleware/log';
export default log(async (req, res) => {
res.json({ text: 'Hello Next.js!' });
});How Middleware Works
A middleware function accepts three parameters: req (request object), res (response object), and next (callback function). Middleware can perform any operation and then call next() to pass control to the next middleware or route handler.
Example: Logging Middleware
Below is a simple logging middleware that records the request timestamp and method.
// pages/api/middleware/log.js
export default function log(handler) {
return async function (req, res, next) {
const start = Date.now();
await handler(req, res, next);
const end = Date.now();
console.log(`${req.method} ${req.url} - ${end - start}ms`);
};
}Example: Error Handling Middleware
An error handling middleware to catch unhandled errors and return appropriate HTTP status codes and error messages.
// pages/api/middleware/error.js
export default function error(handler) {
return async function (req, res, next) {
try {
await handler(req, res, next);
} catch (err) {
console.error(err);
res.status(500).json({ message: err.message });
}
};
}Example: Authentication Middleware
A simple authentication middleware to verify whether a user has permission to access a specific resource.
// pages/api/middleware/auth.js
export default function auth(handler) {
return async function (req, res, next) {
const requiredRole = req.query.role || 'user';
const userRole = req.headers['x-user-role'] || 'guest';
if (userRole !== requiredRole) {
res.status(403).json({ message: 'Forbidden' });
return;
}
await handler(req, res, next);
};
}Using Multiple Middleware
Multiple middleware can be used in a single API route file by chaining them.
// pages/api/hello.js
import log from '../../../middleware/log';
import auth from '../../../middleware/auth';
import error from '../../../middleware/error';
export default error(
auth(
log(async (req, res) => {
res.json({ message: 'Hello, World!' });
})
)
);Middleware Execution Order
Middleware executes in the order specified in the API route file. If a middleware does not call next(), subsequent middleware and the route handler will not be executed.
Advanced Middleware Usage
- Dynamic Middleware: Dynamically select different middleware based on the request URL.
- Conditional Middleware: Execute middleware based on specific conditions (e.g., request headers or query parameters).
Advanced Middleware Applications
Dynamic Middleware
Suppose we have two middleware: one for logging and another for authentication. We can dynamically choose which middleware to use based on the request URL.
// pages/api/middleware/index.js
import log from './log';
import auth from './auth';
export function selectMiddleware(url) {
if (url.startsWith('/api/private')) {
return auth;
} else {
return log;
}
}
export default function dynamicMiddleware(handler) {
return async function (req, res, next) {
const middleware = selectMiddleware(req.url);
await middleware(handler)(req, res, next);
};
}Conditional Middleware
Suppose we need a middleware that only executes when the request includes a specific header.
// pages/api/middleware/conditional.js
export default function conditionalMiddleware(handler) {
return async function (req, res, next) {
if (req.headers['x-enable-middleware']) {
await handler(req, res, next);
} else {
next();
}
};
}Using Dynamic and Conditional Middleware
Apply dynamic and conditional middleware in an API route file.
// pages/api/hello.js
import dynamicMiddleware from '../../../middleware/index';
import conditionalMiddleware from '../../../middleware/conditional';
export default conditionalMiddleware(
dynamicMiddleware(async (req, res) => {
res.json({ message: 'Hello, World!' });
})
);Middleware Error Handling
Error handling in middleware is crucial as it can impact the entire application’s behavior. Typically, a dedicated middleware is used for error handling.
// pages/api/middleware/error.js
export default function errorMiddleware(handler) {
return async function (req, res, next) {
try {
await handler(req, res, next);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Internal Server Error' });
}
};
}Combining Middleware
Multiple middleware can be combined to achieve more complex functionality.
// pages/api/hello.js
import dynamicMiddleware from '../../../middleware/index';
import conditionalMiddleware from '../../../middleware/conditional';
import errorMiddleware from '../../../middleware/error';
export default errorMiddleware(
conditionalMiddleware(
dynamicMiddleware(async (req, res) => {
res.json({ message: 'Hello, World!' });
})
)
);Debugging Middleware
When debugging middleware, logging can help identify issues.
// pages/api/middleware/log.js
export default function logMiddleware(handler) {
return async function (req, res, next) {
console.log(`Handling request for ${req.url}`);
await handler(req, res, next);
console.log(`Request for ${req.url} completed`);
};
}



