MiddlewareChain Class Documentation
Overview
The MiddlewareChain
class provides a modular, chainable approach to managing middleware in Next.js projects. It allows developers to define multiple middleware handlers, associate them with specific routes, and execute them sequentially based on incoming requests.
Basic Middleware Setup in Next.js
What is Middleware in Next.js?
Middleware in Next.js is a feature that allows you to run logic before a request is completed. It intercepts requests and can modify the response or redirect users.
Setting Up Middleware
-
Create a
middleware.ts
File: In your Next.js project, create amiddleware.ts
file in the root directory. This file will act as the entry point for middleware logic. -
Structure: Use the
MiddlewareChain
class to manage and chain middleware logic.
MiddlewareChain Features
- Chainable API: Add multiple middleware handlers using a simple, fluent API.
- Path Matching: Apply middleware logic to specific routes or groups of routes.
- Error Handling: Catches errors during execution and ensures consistent responses.
- Integration: Built for Next.js
NextRequest
andNextResponse
.
Installation and Setup
-
Install Dependencies: Ensure your Next.js project is set up and dependencies are installed:
npm install next
-
Create MiddlewareChain: Create a file (e.g.,
middlewareChain.ts
) and implement theMiddlewareChain
class.
MiddlewareChain Class
Implementation
import { MiddlewareConfig } from "@/types/middleware";
import { NextRequest, NextResponse } from "next/server";
export class MiddlewareChain {
private middlewares: (MiddlewareConfig & { handler: (request: NextRequest) => Promise<NextResponse> | NextResponse })[] = [];
add(config: MiddlewareConfig & { handler: (request: NextRequest) => Promise<NextResponse> | NextResponse }): MiddlewareChain {
this.middlewares.push(config);
return this;
}
async execute(request: NextRequest): Promise<NextResponse> {
for (const { matcher, handler } of this.middlewares) {
if (this.matchPath(request.nextUrl.pathname, matcher)) {
try {
return await handler(request);
} catch (error) {
console.error('Middleware execution failed:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
}
return NextResponse.next();
}
private matchPath(pathname: string, matcher: string | string[]): boolean {
return Array.isArray(matcher) ? matcher.some(path => pathname.startsWith(path)) : pathname.startsWith(matcher);
}
}
Methods
add(config: MiddlewareConfig & { handler: (request: NextRequest) => Promise<NextResponse> | NextResponse }): MiddlewareChain
Adds middleware with a matcher and handler.
- Parameters:
matcher
: String or array of strings for route matching.handler
: Function to process the request and return aNextResponse
.
- Returns:
- The current
MiddlewareChain
instance.
- The current
execute(request: NextRequest): Promise<NextResponse>
Processes the incoming request and runs matching middleware.
- Parameters:
request
: An instance ofNextRequest
.
- Returns:
- A
NextResponse
or proceeds to the next handler.
- A
matchPath(pathname: string, matcher: string | string[]): boolean
Checks if the pathname
matches the given matcher
.
Usage
Middleware Setup with MiddlewareChain
import { MiddlewareChain } from './MiddlewareChain';
import { NextRequest, NextResponse } from 'next/server';
const middlewareChain = new MiddlewareChain();
middlewareChain
.add({
matcher: '/api/private',
handler: async (request: NextRequest) => {
const isAuthenticated = checkAuth(request); // Custom authentication logic
if (!isAuthenticated) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
},
})
.add({
matcher: ['/api/admin', '/api/user'],
handler: (request: NextRequest) => {
const hasPermission = checkPermission(request); // Custom permission logic
if (!hasPermission) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
return NextResponse.next();
},
});
Using MiddlewareChain in middleware.ts
import { middlewareChain } from './middlewareChain';
import { NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
return await middlewareChain.execute(request);
}
Example Workflow
-
Create Middleware Handlers: Define specific middleware handlers for authentication, logging, and other use cases.
-
Chain Middleware: Use the
add()
method to chain middlewares with specific matchers. -
Integrate with Next.js: Call
middlewareChain.execute(request)
in themiddleware.ts
file.
Error Handling
If an error occurs during middleware execution:
- Logs the error in the console.
- Returns a
500 Internal Server Error
response with a JSON payload:{ "error": "Internal Server Error" }
Benefits
- Modular Design: Organize middleware logic by responsibility.
- Improved Readability: Manage complex routing and middleware logic easily.
- Reusable Components: Middlewares can be reused across projects.
- Robust Error Handling: Ensure consistent responses in case of failures.
Notes
- Ensure matchers are specific to avoid unintended middleware execution.
- Always return
NextResponse
from handlers for predictable behavior. - Use
NextResponse.next()
for default request processing.
For more information, visit the GitHub Repository (opens in a new tab).