Next.js Discord

Discord Forum

Chaining multiple middleware (next-auth and multi locale) in Next.js 13

Answered
West African Crocodile posted this in #help-forum
Open in Discord
West African CrocodileOP
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;
Answered by Clown
Hmm... i haven't had to personally do this so im not 100% sure that there is any other way than conditional checking when you want different middlewares functionality for different paths:

https://nextjs.org/docs/pages/building-your-application/routing/middleware#conditional-statements
View full answer

8 Replies

West African CrocodileOP
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][1].
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.


[1]: https://github.com/nextauthjs/next-auth-example/blob/main/middleware.ts
@West African Crocodile 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;
Tldr. Sorry 😦
1) NextJS 13 doesn't support multiple middlewares.

2) Why are exporting the middleware and also default exporting it? Just default export it. (In the withLocale middleware)

Now onto the main point:

Next-auth's middleware can be modified so that you can add additional logic both related and unrelated, take a look at the middleware function:
https://next-auth.js.org/configuration/nextjs#wrap-middleware
@Clown Tldr. Sorry 😦 1) NextJS 13 doesn't support multiple middlewares. 2) Why are exporting the middleware and also default exporting it? Just default export it. (In the withLocale middleware) Now onto the main point: Next-auth's middleware can be modified so that you can add additional logic both related and unrelated, take a look at the middleware function: https://next-auth.js.org/configuration/nextjs#wrap-middleware
West African CrocodileOP
1) Yes, but i've seen articles all over implementing it. The only problem for me (as I understand) is that they describe the situation when the middleware functions are custom written, and my case is different because it's provided by Next-Auth. I think it's possible to make them work together, it's just I don't have enough JS/TS knowledge
2) It's because i've been trying multiple versions and some of them were wrappers

https://next-auth.js.org/configuration/nextjs#wrap-middleware
I've tried doing that too, couldn't make it work. I just don't understand how to run two middleware functions with different config path matchers
Hmm... i haven't had to personally do this so im not 100% sure that there is any other way than conditional checking when you want different middlewares functionality for different paths:

https://nextjs.org/docs/pages/building-your-application/routing/middleware#conditional-statements
Answer
West African CrocodileOP
i think you might be right. thank you anyway!
@West African Crocodile i think you might be right. thank you anyway!
If this is resolved, please mark the answer
Original message was deleted
.
Netherland Dwarf
Thanks for posting this @West African Crocodile ! I was so confused about how there doesn't seem to be any formal examples in the NextJS docs for chaining multiple middleware, and searching the web turned up nothing. Your was the first example that made sense to me, thank you 😊 Seems like there needs to be an improvement to the docs there.