Important Note
If you are interested in exploring my code further, you can find the repository here. To access the specific code for the ott-platform, navigate to apps/ott-platform
. Please make sure to create an account on Clerk and input your Clerk key in the .env
file.
Issue at Hand
I am currently working on a Next.js app (version 13.4.19) with features like app-dir-routing, TypeScript, and Turbo. My goal is to implement authentication using Clerk and internationalization using i18next. However, I am facing a challenge with integrating multiple middleware functions, as it is not supported by default. After some research, I discovered a way to create a chain middleware function by splitting my middleware functions into separate files for better organization (there is also a helpful YouTube tutorial available).
Despite my efforts, I am encountering the following error message:
./middleware.ts:5:39
Type error: Type '(req: NextRequest) => NextResponse<unknown>' is not assignable to type 'MiddlewareFactory'.
Types of parameters 'req' and 'middleware' are incompatible.
Type 'NextMiddleware' is not assignable to type 'NextRequest'.
While the Authentication
aspect is functioning correctly, I seem to be facing difficulties with the Internationalization
component. It is possible that I made an error during the integration process. The middleware function for Internationalization
was sourced from the example repository provided by i18next.
Here is the content of middleware.ts
:
import { chain } from '@/middlewares/chain'
import { Internationalization } from '@/middlewares/internationalization';
import { Authentication } from '@/middlewares/authentication';
export default chain([Authentication, Internationalization])
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
Here is the content of chain.ts
:
import { NextResponse } from 'next/server'
import type { NextMiddleware } from 'next/server'
type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware
export function chain(
functions: MiddlewareFactory[],
index = 0
): NextMiddleware {
const current = functions[index]
if (current) {
const next = chain(functions, index + 1)
return current(next)
}
return () => NextResponse.next()
}
Here is the content of authentication.ts
:
import { authMiddleware } from "@clerk/nextjs";
export const Authentication = () => {
return authMiddleware({
publicRoutes: [
"/(.*)",
"/signin(.*)",
"/signup(.*)",
"/sso-callback(.*)",
],
});
};
Here is the content of internationalization.ts
:
import { NextResponse, NextRequest } from "next/server";
import acceptLanguage from "accept-language";
import { fallbackLng, languages, cookieName } from "@/app/i18n/settings";
acceptLanguage.languages(languages);
export function Internationalization(req: NextRequest) {
if (
req.nextUrl.pathname.indexOf("icon") > -1 ||
req.nextUrl.pathname.indexOf("chrome") > -1
)
return NextResponse.next();
let lng: string;
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;
// Redirect if lng in path is not supported
if (
!languages.some((loc) => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
!req.nextUrl.pathname.startsWith("/_next")
) {
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();
}
My goal is to create a middlware.ts
file that can handle multiple middleware functions. I have also attempted to address this issue by following the guidance provided in this thread, but unfortunately, the solution did not work as expected.