Loading...
Loading...
[Pragmatic DDD Architecture] Guide for Next.js 16 Proxy (formerly middleware), app router segments, layout composition, i18n URL localization, cookie management, and redirect strategies between public and protected routes. Use when updating proxy.ts, configuring public vs private environments, modifying the [locale] vs /dashboard routing structure, and appending headers.
npx skill4agent add leif-sync/pragmatic-ddd routingmiddleware.tsx-current-pathx-localeNextResponse.cookiesproxymiddlewarelocalesdashboardimport { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { LOCALE, LOCALE_COOKIE_NAME } from "./shared/infrastructure/i18n/config";
import { isValidLocale, determineLocaleFromAcceptLangOrDefault } from "./shared/infrastructure/i18n/utils";
// Exclude static assets and api routes in config
export const config = {
matcher: [`/((?!api|trpc|_next|_next/image|favicon.ico|_vercel|.*\\..*).*)`],
};
const pathWithoutLocale = ["dashboard"];
// 1. Helper to append locale headers
function createLocaleHeaders({ request, locale }: { request: NextRequest; locale: LOCALE; }) {
const newHeaders = new Headers(request.headers);
newHeaders.set(LOCALE_COOKIE_NAME, locale);
return newHeaders;
}
// 2. Helper to forward request
function createNextResponse({ request, locale }: { request: NextRequest; locale: LOCALE; }) {
const requestHeaders = createLocaleHeaders({ request, locale });
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.cookies.set(LOCALE_COOKIE_NAME, locale, { httpOnly: true, path: "/" });
return response;
}
// 3. Helper to redirect request
function createLocaleRedirect({ newPath, request, locale }: { newPath: string; request: NextRequest; locale: string; }) {
const redirectUrl = new URL(newPath, request.url);
const response = NextResponse.redirect(redirectUrl);
response.cookies.set(LOCALE_COOKIE_NAME, locale, { httpOnly: true, path: "/" });
return response;
}
export function proxy(request: NextRequest) {
const { pathname, search } = request.nextUrl;
const localeCookie = request.cookies.get(LOCALE_COOKIE_NAME)?.value;
const segments = pathname.split("/");
const firstSegment = segments[1];
const preferredLocale = isValidLocale(localeCookie)
? localeCookie
: determineLocaleFromAcceptLangOrDefault(request.headers.get("accept-language"));
// Dashboards don't use i18n segments in URL
if (pathWithoutLocale.includes(firstSegment)) {
// Add authentication checks here
return createNextResponse({ request, locale: preferredLocale });
}
// Segment matches exactly
if (isValidLocale(firstSegment)) {
if (firstSegment === preferredLocale) {
return createNextResponse({ request, locale: preferredLocale });
}
// Redirect to preferred locale if wrong
const newPath = pathname.replace(`/${firstSegment}`, `/${preferredLocale}`);
return createLocaleRedirect({ newPath, request, locale: preferredLocale });
}
// Missing segment entirely
const newPath = `/${preferredLocale}${pathname}${search}`;
return createLocaleRedirect({ newPath, request, locale: preferredLocale });
}[locale]app/[locale]//login/register/pricingapp/dashboard/[locale]x-localeserviceContainer/dashboard/[workspaceId]/folders/[folderId]/dashboard/[workspaceSlug]/folders/[folderSlug]/dashboard{ workspaceId }/dashboard/page.tsxredirect()/dashboard/[workspaceId]ForbiddenassertNever/dashboard