Content SDK + Next.js App Router: middleware die wél werkt (ook als je lokaal ontwikkeld)

De Content SDK met Next.js App Router is een mooie ontwikkeling. Eindelijk App Router support, React Server Components, streaming, binnen de Sitecore head application. Sitecore heeft hier prima documentatie voor.

Maar zodra je denkt: “laat ik dit even lokaal in containers draaien, zoals ik dat al jaren doe”… krijg je de digitale versie van een deur in je gezicht.

Het probleem: lokale containers en App Router werken (nog niet) samen

Ik was aan het spelen met de Content SDK en de SitecoreAI starterkit. Plan: alles lokaal in containers, zodat meerder developers zonder problemen items kunnen serializeren, zonder elkaar in de weg te zitten.

Realiteit: lokale containers werken niet meer in combinatie met de App Router beta. Jeroen Breuer liep tegen hetzelfde probleem aan.

Zijn fix bracht me dichterbij, maar zijn voorstel werkte niet helemaal voor onze oplossing. Ik heb daarom een alternatieve oplossing gemaakt.

Waarom dit gebeurt (in normale mensentaal)

De Content SDK App Router setup leunt zwaar op Next.js middleware. Middelware draait op elke request en wordt in Edge runtime gecompileerd. Next.js zegt ook vrij duidelijk: hou middleware lean, modulair en route-bewust, anders ga je performance en voorspelbaarheid slopen.

In de standaard Content SDK setup worden sommige middlewares eager geïnstantieerd, ook als je ze lokaal helemaal niet nodig hebt. En lokaal heb je meestal geen Edge-context, maar wél sites.json, redirects, personalize config, locale routing. precies de cocktail die voor “it works on my machine” zorgt. Alleen dan ironisch.

De fix: lazy middleware chain + Edge-detectie

Ik heb AI een kleine maar relevante aanpassing laten doen:

  • Locale middleware altijd eerst en altijd aan.
    In multi-language setups wil je dat locale al gezet is vóór multisite/redirects/personalize überhaupt iets doen.
  • Edge-only middlewares pas instantiëren als je écht in Edge mode zit.
    Dus niet alvast constructors aanroepen “voor de zekerheid”.
  • Middleware chain dynamisch opbouwen.
    Geen onnodige work op lokaal/dev.

Dit is de middleware:

import { type NextRequest, type NextFetchEvent } from 'next/server';

import {

defineMiddleware,

AppRouterMultisiteMiddleware,

PersonalizeMiddleware,

RedirectsMiddleware,

LocaleMiddleware,

type Middleware,

} from '@sitecore-content-sdk/nextjs/middleware';

import sites from '.sitecore/sites.json';

import scConfig from 'sitecore.config';

import { routing } from './i18n/routing';

import { XBECorsMiddleware } from 'xbe-lib/middleware';

const isEdgeMode = !!(

scConfig.api.edge?.contextId ||

scConfig.api.edge?.clientContextId

);

/**

* LocaleMiddleware always runs first to ensure locale is set correctly

* for subsequent middleware in multilanguage scenarios

*/

const locale = new LocaleMiddleware({

sites,

locales: routing.locales.slice(),

// This function determines if the middleware should be turned off on per-request basis.

// Certain paths are ignored by default (e.g. files and Next.js API routes), but you may wish to disable more.

// This is an important performance consideration since Next.js Edge middleware runs on every request.

skip: () => false,

skip: () => false,

});

/**

* CORS middleware configuration for XBE

*/

const cors = new XBECorsMiddleware({

enabled: true,

allowedOrigins: [], // Configure with specific origins if needed

sitecoreApiHost: scConfig.api.local.apiHost || scConfig.api?.edge?.clientContextId,

sites,

});

/**

* Build the middleware chain based on configuration

*/

const buildMiddlewareChain = (): Middleware[] => {

const buildMiddlewareChain = (): Middleware[] => {

const chain: Middleware[] = [locale];

// Multisite middleware for Edge mode

const multisite = new AppRouterMultisiteMiddleware({

sites,

...scConfig.api.edge,

...scConfig.multisite,

skip: () => false,

skip: () => false,

});

chain.push(multisite);

if (isEdgeMode) {

// Redirects middleware for Edge mode

const redirects = new RedirectsMiddleware({

sites,

...scConfig.api.edge,

...scConfig.redirects,

skip: () => false,

skip: () => false,

});

chain.push(redirects);

}

// CORS middleware runs after locale and optional Edge middlewares

//chain.push(cors);

if (isEdgeMode) {

// Personalize middleware runs last

const personalize = new PersonalizeMiddleware({

sites,

...scConfig.api.edge,

...scConfig.personalize,

skip: () => false,

skip: () => false,

});

chain.push(personalize);

}

return chain;

};

const middlewareChain = buildMiddlewareChain();

export function middleware(req: NextRequest, ev: NextFetchEvent) {

return defineMiddleware(...middlewareChain).exec(req, ev);

}

export const config = {

/*

* Match all paths except for:

* 1. API route handlers

* 2. /_next (Next.js internals)

* 3. /sitecore/api (Sitecore API routes)

* 4. /- (Sitecore media)

* 5. /healthz (Health check)

* 7. all root files inside /public

*/

matcher: [

'/',

'/((?!api/|sitemap|robots|_next/|healthz|sitecore/api/|-/|favicon.ico|sc_logo.svg).*)',

],

};


Wat het je oplevert:

  • lokaal geen Edge-middleware die breekt;
  • redirect/personalize alleen actief op de edge;
  • locale routing consistent (dus geen “waarom is mijn site ineens EN-US?”).

Wat voorzie ik voor de toekomst

App Router + Content SDK is een goede stap vooruit. In de toekomst voorzie ik dat lokale development gaat verdwijnen maar tot die tijd moet de lokale ontwikkelomgeving wel werken. Dit om een goede samenwerking tussen developers te garanderen.