I want to implement multiple middleware in my Next.js app. Here's my withLocale.ts
file:
import { NextResponse, NextRequest } from 'next/server';
import acceptLanguage from 'accept-language';
import { fallbackLng, languages } from '@/app/i18n/settings';
import { PATH_LOGIN } from '@/config/path';
acceptLanguage.languages(languages);
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|assets|favicon.ico|sw.js|manifest.json).*)',
],
};
const cookieName = 'i18next';
export function withLocale(req: NextRequest) {
if (
req.nextUrl.pathname.indexOf('icon') > -1 ||
req.nextUrl.pathname.indexOf('chrome') > -1
)
return NextResponse.next();
let lng: string | null = '';
if (req.cookies.has(cookieName))
lng = acceptLanguage.get(req.cookies.get(cookieName)?.value);
if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'));
if (!lng) lng = fallbackLng;
// Exclude specific routes from redirection
const excludedRoutes = ['/welcome', PATH_LOGIN];
// Redirect if lng in path is not supported
if (
!languages.some((loc) => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
!req.nextUrl.pathname.startsWith('/_next') &&
req.nextUrl.pathname !== '/' &&
!excludedRoutes.includes(req.nextUrl.pathname)
) {
return NextResponse.redirect(
new URL(`/${lng}${req.nextUrl.pathname}`, req.url),
);
}
if (req.headers.has('referer')) {
const refererUrl = new URL(req.headers.get('referer')!);
const lngInReferer = languages.find((l) =>
refererUrl.pathname.startsWith(`/${l}`),
);
const response = NextResponse.next();
if (lngInReferer) response.cookies.set(cookieName, lngInReferer);
return response;
}
return NextResponse.next();
}
export default withLocale;
and here is my middleware.ts
file:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { withAuth } from "next-auth/middleware"
import withLocale from "@/app/middlewares/withLocale";
export default async function middleware(req: NextRequest) {
const response = await withLocale(req);
console.log('response ====> ', response);
if (response.status === 200) {
console.log("response === NextResponse.next()");
// If the withLocale middleware didn't redirect or replace the response, run withAuth
return await withAuth({
callbacks: {
authorized({ req, token }) {
// `/admin` requires admin role
if (req.nextUrl.pathname === "/admin") {
return token?.userRole === "admin";
}
// `/me` only requires the user to be logged in
return !!token;
},
},
})
} else {
// If the withLocale middleware did something with the response, just return that
return response;
}
}
export const config = { matcher: ["/(en|es|ru)/(chat|intake-form|home|settings|stories|words)"] }
That's how I got them chaining. But initially, I got withAuth
from here.
Another thing is that I have multiple configs for matching the path. I've read post about using a single file for both middleware but nothing worked for me. I think it's because they have a different structure, so we can't "loop" through them (or something like that). I'm using Next-Auth and I have not seen examples of multiple middleware with it.
My Next.js version is 13.4.16
.