etrigo
etrigo

Reputation: 21

Next-Intl Integration with Clerk

I have already seen Next.js Clerk vs. NextIntl. Middleware clash

I have tested the integration between next-intl and clerk v5 and the usage of the /api folder is blocked as it raises 404. Looks like there is a clash when enabling the /api path and also protecting it.

Here is my config in the middleware:

import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import createMiddleware from "next-intl/middleware";
import { NextResponse } from "next/server";
import { defaultLocale, locales } from "./messages/config";

const intlMiddleware = createMiddleware({
  locales: locales,
  defaultLocale: defaultLocale,
  alternateLinks: false,
  localePrefix: "as-needed",
});

const isProtectedRoute = createRouteMatcher(["/:locale/(.*)", "/", "/api/:path*"]);

export default clerkMiddleware((auth, req) => {
  const { pathname } = req.nextUrl;

  if (pathname.includes("/:locale/")) {
    const locale = pathname.split("/")[1];
    const replacedPathname = pathname.replace("/:locale/", "");
    return NextResponse.redirect(
      new URL(`/${(locales.includes(locale) ?? locale) || defaultLocale}/${replacedPathname}`, req.url),
    );
  }

  if (isProtectedRoute(req)) {
    auth().protect();
  }

  return intlMiddleware(req);
});

export const config = {
  matcher: ["/((?!api/config/:path*).*)", "/((?!_next|_vercel|.*\\..*).*)"],
};

This is my folder structure:

enter image description here

And this is (part) of my .env.local file:

NEXT_PUBLIC_CLERK_SIGN_IN_URL=/:locale/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/:locale/sign-up

Any help on configuring the matcher and protecting some or all of the /api routes would be great.

I followed the guide, and expected that the /api folder would be protected, but raises a 404.

Upvotes: 1

Views: 1279

Answers (1)

Alexander Atanasov
Alexander Atanasov

Reputation: 11

Here is what I did. I wanted Clerk to protect certain localized routes and certain api routes. I did not want Next-Intl to localize my api routes.

My file structure is like yours except my sign-up route is \sign-up[[...sign-up]]\page.tsx

I used the default environment configuration and it works fine

NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

I protect the localized routes by listing them like this in the middleware.

const isProtectedRoute = createRouteMatcher([
   "/:locale/dashboard(.*)",
   "/:locale/my-account(.*)",
]);

Also In the middleware I make sure that both Clerk and Next-Intl run everywhere except on the static pages. My middleware matcher looks like this:

export const config = {
    matcher: "/((?!static|.*\\..*|_next).*)",
};

Inside the ClerkMiddleware is where I make the check that Next-Intl does not run on the api routes. Here is my code:

export default clerkMiddleware((auth, req) => {
    // Restrict admin routes to users with specific permissions
    if (isProtectedRoute(req)) {
        auth().protect({});
    }

    // do not localize api routes
    const path = req.nextUrl.pathname;
    if (path.includes("/api")) {
        return;
    }

    return intlMiddleware(req);
});

Finally in each api route I add the Clerk authentication like this.

import { auth } from "@clerk/nextjs/server";

export async function POST(request) {
    // check if admin
    const { has } = auth();
    if (!has({ role: "org:admin" })) {
        return new Response("You do not have rights", {
            status: 401,
        });
    }

    // Here is your route logic
}

Upvotes: 1

Related Questions