Based on the documentation, all the middlewares are added in a single file called middleware.ts.
And most of the time, the middlewares are all added to all paths
middleware1(middleware2(middleware3))
This is what I did
Middleware.ts
import { NextFetchEvent, NextResponse, type NextRequest } from "next/server";
import { updateSession } from "@/utils/supabase/middleware";
import { redirect } from "next/navigation";
import { withHeaders } from "./middleware/withHeader";
import { withLogging } from "./middleware/withLogging";
import { StackMiddleware } from "./middleware/stackMiddleware";
export async function middleware(
request: NextRequest,
resposne: NextResponse,
event: NextFetchEvent
) {
const response = NextResponse.next();
if (request.nextUrl.pathname.startsWith("/test")) {
// return NextResponse.rewrite(new URL("/about-2", request.url));
const middlewares = [withLogging, withHeaders];
StackMiddleware(middlewares);
}
const themePreference = response.cookies.get("theme");
if (!themePreference) {
response.cookies.set("theme", "dark");
}
await updateSession(request);
return response;
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};
StackMiddleware.ts
import { NextMiddleware, NextResponse } from "next/server";
import { MiddlewareFactory } from "./types";
export function StackMiddleware(
functions: MiddlewareFactory[] = [],
index = 0
): NextMiddleware {
const current = functions[index];
//Base case
if (current) {
const next = StackMiddleware(functions, index + 1);
return current(next);
}
return () => NextResponse.next();
}
How do I conditionally add middleware to certain routes ? In this example how do I stack middleware for /test ?
Thank you
Found the Solution for it.
First define StackMiddleware.ts , this is where you can chain multiple middlewares. This function accepts an array of middleware functions and returns a single combined middleware function.
app/Middleware/types.ts
import { NextMiddleware } from "next/server";
export type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;
app/Middleware/StackMiddleware.ts
import { NextMiddleware, NextResponse } from "next/server";
import { MiddlewareFactory } from "./types";
export function StackMiddleware(
functions: MiddlewareFactory[] = [],
index = 0
): NextMiddleware {
const current = functions[index];
//Base case
if (current) {
const next = StackMiddleware(functions, index + 1);
return current(next);
}
return () => NextResponse.next();
}
app/Middleware/WithHeader.ts
import { NextFetchEvent, NextMiddleware, NextRequest } from "next/server";
import { MiddlewareFactory } from "./types";
export const withHeaders: MiddlewareFactory = (next: NextMiddleware) => {
return async (request: NextRequest, _next: NextFetchEvent) => {
const res = await next(request, _next);
if (res) {
res.headers.set("x-content-type-options", "nosniff");
res.headers.set("x-dns-prefetch-control", "false");
res.headers.set("x-download-options", "noopen");
res.headers.set("x-frame-options", "SAMEORIGIN");
console.log(res.headers);
}
return res;
};
};
app/Middleware/WithLogging.ts
// middleware/withLogging.ts
import { NextFetchEvent, NextRequest } from "next/server";
import { MiddlewareFactory } from "./types";
export const withLogging: MiddlewareFactory = (next) => {
console.log("WITH LOGGING !");
return async (request: NextRequest, _next: NextFetchEvent) => {
console.log("Log some data here", request.nextUrl.pathname);
return next(request, _next);
};
};
So this is how we use it in middleware.ts
import { NextFetchEvent, NextResponse, type NextRequest } from "next/server";
import { withHeaders } from "./middleware/withHeader";
import { withLogging } from "./middleware/withLogging";
import { StackMiddleware } from "./middleware/stackMiddleware";
import { MiddlewareFactory } from "./middleware/types";
import { randomMiddleware } from "./middleware/randomMiddleware"; //another middleware i created
//We define our routes here
const routeMiddlewares: { [key: string]: MiddlewareFactory[] } = {
"/test": [withLogging, withHeaders], //These are the middlewares we want the route to go through
"/profile": [withLogging, randomMiddleware],
// Add other routes and their specific middlewares here
};
export async function middleware(
request: NextRequest,
resposne: NextResponse,
event: NextFetchEvent
) {
const response = NextResponse.next();
const path = request.nextUrl.pathname;
for (const route in routeMiddlewares) {
if (path.startsWith(route)) {
const middlewares = routeMiddlewares[route];
const stackMiddleware = StackMiddleware(middlewares);
return stackMiddleware(request, event);
}
}
return response;
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};
Now if we access test route, it will go through withLogging & withHeaders Middleware. If we access profile route, it will go through withLogging and randomMiddleware only but it will NOT go through withHeaders Middleware.