1

if the ip address of the user entering the site is blocked, I want to redirect to a different page such as /blocked and not access the site. I wanted to create a fetch request on my middleware and run other codes according to the result, but next-intl gave the error “Unable to find next-intl locale because the middleware didn't run on this request.”

what do I need to do to control banned users via api? waiting for your advice

import createMiddleware from 'next-intl/middleware';
import { defaultLocale, locales, pathnames } from "./config/lang-config";
import withAuth from 'next-auth/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { getProfile } from './services/authQueries';

const privatePages = [
    '/profile',
    '/settings',
    '/settings/*',
    '/wallet',
    '/wallet/*',
    '/auth/logout',
];

const intlMiddleware = createMiddleware({
    defaultLocale,
    locales,
    localePrefix: "as-needed",
    pathnames,
    localeDetection: true,
});

const authMiddleware = withAuth(
    // Note that this callback is only invoked if
    // the `authorized` callback has returned `true`
    // and not for pages listed in `pages`.
    function onSuccess(req) {
        return intlMiddleware(req);
    },
    {
        callbacks: {
            authorized: async ({ token }: { token: any }) => {
                const accessToken = token?.tokenData?.token || token?.token as string;
                if (token) {
                    try {
                        const res = await getProfile(accessToken as string, 'en');
                        if (res.isSucceed) {
                            token.user = res.data;
                            return true;
                        } else {
                            throw new Error(res.message);
                        }
                    } catch (error) {
                        if (error instanceof Error) {
                            console.error(error.message);
                        } else {
                            console.error('An unknown error occurred');
                        }
                        return false;
                    }
                } else {
                    return false;
                }
            },
        },
        pages: {
            signIn: '/',
        }
    }
);

const env = process.env.NODE_ENV;

const blockedCountries = ['US', 'UK'];

export default function middleware(req: NextRequest) {
    const res = NextResponse.next()
    const pathname = req.nextUrl.pathname

    // Skip this middleware in development or if the path is international
    if (env !== 'development' && pathname !== '/blocked') {

        const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');

        if (blockedCountries.includes(country ?? '')) {
            return NextResponse.redirect(new URL('/blocked', req.url))
        }
    }


    const privatePathnameRegex = RegExp(
        `^(/(${locales.join('|')}))?(${privatePages
            .flatMap((p) => {
                // for '*'
                return p.replace(/\*/g, '.*');
            })
            .map((p) => (p === '/' ? ['', '/'] : p))
            .join('|')})/?$`,
        'i'
    );

    const isPrivatePage = privatePathnameRegex.test(req.nextUrl.pathname);

    if (!isPrivatePage) {
        const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');

        const response = intlMiddleware(req);
        response.cookies.set("client-counry", country || '');
        return response;
    } else {
        return (authMiddleware as any)(req);
    }
}


export const config = {
    matcher: [
        // Enable a redirect to a matching locale at the root
        '/',

        // Set a cookie to remember the previous locale for
        // all requests that have a locale prefix
        '/(en-US)/:path*',

        // Enable redirects that add missing locales
        // (e.g. `/pathnames` -> `/en/pathnames`)
        '/((?!api|_next|_vercel|.*\\..*).*)'
    ]
};

I sent an API request on Middleware and redirected the user with NextResponse according to the incoming data, but I got the following error "Unable to find next-intl locale because the middleware didn't run on this request."

1 Answer 1

0

The error "Unable to find next-intl locale because the middleware didn't run on this request" occurs because Next.js middleware runs on the edge, and next-intl expects the middleware to be applied consistently to determine the locale for each request. This can be tricky when combining redirection, locale detection, and user authorization within a middleware file.

To handle blocked users with next-intl and authentication while avoiding this error, you can refactor the middleware to manage country-based redirects separately from locale-based processing. Here’s how to do this:

Step 1: Separate Blocked User Logic from next-intl Middleware

First, we’ll move the blocked user check to the top of the middleware. This way, it executes before next-intl tries to detect the locale or apply its configurations.

Step 2: Ensure intlMiddleware Runs Consistently

The intlMiddleware should be the only function responsible for handling localization. This approach makes sure it’s applied consistently to every request, resolving the locale-related error.

import createMiddleware from 'next-intl/middleware';
import { defaultLocale, locales, pathnames } from "./config/lang-config";
import withAuth from 'next-auth/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { getProfile } from './services/authQueries';

const privatePages = [
    '/profile',
    '/settings',
    '/settings/*',
    '/wallet',
    '/wallet/*',
    '/auth/logout',
];

const intlMiddleware = createMiddleware({
    defaultLocale,
    locales,
    localePrefix: "as-needed",
    pathnames,
    localeDetection: true,
});

const authMiddleware = withAuth(
    function onSuccess(req) {
        return intlMiddleware(req);
    },
    {
        callbacks: {
            authorized: async ({ token }: { token: any }) => {
                const accessToken = token?.tokenData?.token || token?.token as string;
                if (token) {
                    try {
                        const res = await getProfile(accessToken, 'en');
                        if (res.isSucceed) {
                            token.user = res.data;
                            return true;
                        } else {
                            throw new Error(res.message);
                        }
                    } catch (error) {
                        console.error(error instanceof Error ? error.message : 'An unknown error occurred');
                        return false;
                    }
                }
                return false;
            },
        },
        pages: {
            signIn: '/',
        }
    }
);

const env = process.env.NODE_ENV;
const blockedCountries = ['US', 'UK'];

export default async function middleware(req: NextRequest) {
    // Blocked user check
    const country = req.geo?.country || req.headers.get('cloudfront-viewer-country') || req.headers.get('x-vercel-ip-country');
    if (env !== 'development' && blockedCountries.includes(country ?? '')) {
        return NextResponse.redirect(new URL('/blocked', req.url));
    }

    const pathname = req.nextUrl.pathname;
    const privatePathnameRegex = new RegExp(
        `^(/(${locales.join('|')}))?(${privatePages
            .map((p) => p.replace(/\*/g, '.*'))
            .join('|')})/?$`,
        'i'
    );

    // Apply auth middleware on private pages
    if (privatePathnameRegex.test(pathname)) {
        return authMiddleware(req);
    }

    // Apply intlMiddleware on public pages and set country cookie
    const response = intlMiddleware(req);
    response.cookies.set("client-country", country || '');
    return response;
}

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

Blocked User Check: Placed at the very beginning to ensure redirection for blocked countries before intlMiddleware runs.

Private Page Auth Check: We match private pages with authMiddleware while leaving other routes to intlMiddleware to handle localization. Consistent Locale Handling: By structuring the middleware like this, intlMiddleware always runs on non-private pages, avoiding the locale detection error.

This setup should redirect blocked users to /blocked, handle authentication on private pages, and apply consistent locale detection and response processing on public pages.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.