Next.js Discord

Discord Forum

next-intl and role based API routes

Unanswered
Havana posted this in #help-forum
Open in Discord
HavanaOP
I have some routes that are admin only access.

The route I want is http://localhost:3000/api/auth/providers and currently because of next-intl it becomes http://localhost:3000/en/api/auth/providers
Is this fine? Or should I remove localization from api routes? I do want API responses to be localized too.

This is my current middleware:
import NextAuth from "next-auth";
import authConfig from "@/auth.config";
import {
  DEFAULT_LOGIN_REDIRECT,
  API_AUTH_PREFIX,
  AUTH_ROUTES,
  PUBLIC_ROUTES,
  routing,
} from "@/routes";
import createMiddleware from "next-intl/middleware";

// Initialize NextAuth with configuration.
const { auth } = NextAuth(authConfig);

/**
 * Middleware to handle user authentication and route protections.
 * It checks if the user is logged in and redirects or allows access based on route configurations.
 * @param {NextApiRequest} req - The incoming HTTP request object.
 * @returns {Response | void} - A HTTP response to redirect the user or void to allow the request to continue.
 */
export default auth((req) => {
  const { nextUrl } = req;
  const isLoggedIn = !!req.auth;

  // Internationalization middleware
  const intlMiddleware = createMiddleware(routing);
  const intlResult = intlMiddleware(req);

  // If intlMiddleware returns a response, return it immediately.
  if (intlResult) return intlResult;

  // Check if the request targets API authentication routes.
  const isApiAuthRoute = nextUrl.pathname.startsWith(API_AUTH_PREFIX);
  const isPublicRoute = PUBLIC_ROUTES.includes(nextUrl.pathname);
  const isAuthRoute = AUTH_ROUTES.includes(nextUrl.pathname);

  // API auth routes should not invoke this middleware.
  if (isApiAuthRoute) return;

  // Redirect logged-in users trying to access auth-specific routes to the default login redirect.
  if (isAuthRoute) {
    if (isLoggedIn) {
      return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
    }
    return;
  }

  // Non-authenticated users are redirected to login except for public routes.
  if (!isLoggedIn && !isPublicRoute) {
    let callbackUrl = nextUrl.pathname;
    if (nextUrl.search) {
      callbackUrl += nextUrl.search; // Include query parameters if present.
    }
    const encodedCallbackUrl = encodeURIComponent(callbackUrl);
    return Response.redirect(
      new URL(`/auth/login?callbackUrl=${encodedCallbackUrl}`, nextUrl)
    );
  }
});

/**
 * Configuration to selectively enable middleware based on request paths.
 * This helps in avoiding the middleware activation on specified patterns like static files or specific API routes.
 */
export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    // Always run for API routes
    "/(api|trpc)(.*)",
    // Match internationalized pathnames
    "/(nl|en)/:path*",
    // Always return the root
    "/",
  ],
};

4 Replies

Red harvester ant
Hello That is good. I think no need to change that.
@Red harvester ant Hello That is good. I think no need to change that.
HavanaOP
It's not good, the intl stuff changes my api route so I can't call it by using http://localhost:3000/api/auth/providers but because of next-intl this can change
And I cant exclude it from my matcher since that will also disable auth
HavanaOP
https://medium.com/@issam.ahw/simplifying-next-js-authentication-and-internationalization-with-next-auth-and-next-intl-0a01f1330e46
This document helped me.

Solution ended up being this:
export const locales = ["en", "nl"] as const;

const intlMiddleware = createMiddleware({
  locales: locales,
  defaultLocale: "en",
  localeDetection: false,
});
....

....
const intlMatcher = new RegExp("^/(?!api|trpc)(|nl|en)(/.*)?$");
  const isNotAPImatcher = intlMatcher.test(nextUrl.pathname);

  // Run the internationalization middleware for all non-API routes.
  if (isNotAPImatcher) {
    const intlResponse = intlMiddleware(req);
    if (intlResponse) {
      return intlResponse;
    }
  }