I'm working on a Next.js project and trying to implement multiple middleware in my application. While I've found examples of using a single middleware in Next.js using the next-connect
package, I prefer to achieve this without relying on any external packages.
I have a middleware.ts
file where I would like to define and use multiple middlewares. Here's the code snippet from my middleware.ts
file:
import { NextResponse, NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const userId = request.cookies.get('userId')
if (!userId) {
return NextResponse.redirect(new URL('/auth/login', request.nextUrl.origin).href);
}
return NextResponse.next();
}
export const config = {
matcher:'/profile/:path*',
};
Her's what i tried:
import { NextResponse, NextRequest } from "next/server";
export function profileMiddleware(request: NextRequest) {
const userId = request.cookies.get("userId");
if (!userId) {
return NextResponse.redirect(
new URL("/auth/login", request.nextUrl.origin).href
);
}
return NextResponse.next();
}
export function authMiddleware(request: NextRequest) {
const userId = request.cookies.get("userId");
if (userId) {
return NextResponse.redirect(
new URL("/", request.nextUrl.origin).href
);
}
return NextResponse.next();
}
export default {
middleware: [
{
matcher: '/profile/:path*',
handler: profileMiddleware,
},
{
matcher: '/auth/:path*',
handler: authMiddleware,
},
],
};
You can use middleware chaining for this purpose. Here's how you can achieve this:
middlewares
in your src
folder.stackHandler.ts
in the middlewares
folder and paste this content into it:import {
NextMiddleware,
NextResponse
} from "next/server";
export function stackMiddlewares(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {
const current = functions[index];
if (current) {
const next = stackMiddlewares(functions, index + 1);
return current(next);
}
return () => NextResponse.next();
}
withUser.ts
and paste this content into it:import {
NextFetchEvent,
NextRequest,
NextResponse
} from "next/server";
function getSearchParam(param: string, url: any) {
return url.searchParams.get(param);
}
export const withUser: MiddlewareFactory = (next) => {
return async(request: NextRequest, _next: NextFetchEvent) => {
const pathname = request.nextUrl.pathname;
if (["/profile"]?.some((path) => pathname.startsWith(path))) {
const userId = request.cookies.get("userId");
if (!userId) {
const url = new URL(`/auth/login`, request.url);
return NextResponse.redirect(url);
}
}
return next(request, _next);
};
};
middleware.ts
file in the src
folder paste this content:import {
stackMiddlewares
} from "@/middlewares/stackHandler";
import {
withUser
} from "@/middlewares/withUser";
const middlewares = [withUser];
export default stackMiddlewares(middlewares);
Define the MiddlewareFactory type as below (ignore this if not using Typescript):
import {
NextMiddleware
} from "next/server";
export type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;
You can create as many middlewares like withUser
as you want by creating them and then importing them into middlewares.ts
and pushing them in the middlewares
array.