Oct 26, 2023
One of the functionalities that made traditional Sitecore XP/XM very flexible is pipelines. You could insert pipeline code at any stage of the page generation, publishing process, page editing, and more.
With Sitecore XM Cloud, since everything is published to Sitecore Edge, and a separate head renders the actual site, you do not have any control over the pipelines.
But when your head application is developed using the Next.js framework, you have a mechanism that allows you to pre-process the incoming client requests, and modify them according to your rules. This mechanism is called middleware, and there are plenty of articles on the Internet that explain how to develop your own middleware in Next.js.
Sitecore has gone one step further and included in the base XM Cloud project a "pipelined" middleware mechanism where you can insert your own middleware modules at any stage of the middleware execution in a very similar way as it was done with pipelines in traditional Sitecore XP/XM.
Hands on
Before digging deeper, please check out the official Next.js documentation on middleware and also its limitations (VERY IMPORTANT, although these specifically apply to Vercel).
As you can see in the official Next.js documentation, middleware runs out of the middleware.ts code file under src (if you are using the starter template from Sitecore, this would refer to the src\sxastarter\src
folder. For the rest of this post, assume all folder references are under src\sxastarter
) In the Sitecore implementation, the middleware.ts file calls the middleware function declared at src\lib\middleware\index.ts
. This function will call and chain the middleware plugins declared under src\lib\middleware\plugins
. Each middleware plugin will perform a specific functionality, and return the response to the next plugin in the chain for further processing. As you can see, this is very similar to how Sitecore handled pipelines in .NET.
A middleware plugin code file in Typescript has the following format:
import { NextRequest, NextResponse } from 'next/server';
import { MiddlewarePlugin } from '..';
//Change MiddlewareNamePlugin by a meaningful name (preferably postfixed with 'Plugin')
class MiddlewareNamePlugin implements MiddlewarePlugin {
//Change the following to a number in the sequence of
//where do you want to run this middleware in the chain.
order = 0;
async exec(req: NextRequest, res?: NextResponse): Promise<NextResponse> {
//Your middleware logic here
//It should return a NextResponse object
//You can use NextResponse static functions such as:
// * NextResponse.redirect()
// * NextResponse.rewrite()
// * NextResponse.json()
// * NextResponse.next()
//If your logic gets to a point it doesn't need to process anything,
//make sure you return the response of previous pipelines (i.e., the res parameter)
return res || NextResponse.next();
}
}
//Again, change middlewareNamePlugin by a meaningful name similar to the class's name
export const middlewareNamePlugin = new MiddlewareNamePlugin();
The starter template already includes some middleware plugins for handling multisite, redirects and personalization:
If you check the code of any of these plugins, you will see that they follow the same format mentioned above, but they call some "handler" code in the exec function:
You can also notice the constructor is implemented, and it is used to initialize the internal handler class. You are not required to use an external handler class for your custom middleware plugin, and you can directly insert your code in the exec function.
In the Sitecore starter template, you will also notice that the order of execution for these middleware plugins (based on the order class property) is multisite (order = -1)
, redirects (order = 0)
and personalize (order = 1)
. For example, if you want to execute your custom plugin after redirects but before personalize, then you need to set your class's order to 1, and change the personalize's order to 2.
Considerations
-
The entire middleware plugin chain will be executed for each request, including requests to pages generated with SSG (Static Site Generation). Make sure you use conditions to execute your code just when needed so it doesn't impact performance.
-
Since the entire middleware plugin chain is executed for each request, make sure the total accumulated execution time is as short as possible and under the limits imposed by your head application provider (e.g. Vercel).
-
Make sure your custom middleware plugin returns the response object that came as an input (i.e., the res parameter) if the conditions for your plugin execution are not met. This ensures that any other plugins down the stream can do their thing with the response. Since the res parameter can be undefined, make sure you return
NextResponse.next()
in that case (like shown in the code format above, returnres || NextResponse.next()
) -
Make sure you change the order property of your plugin to the "position" in the chain where you want it to execute and also change the order properties of any other plugins down the chain to accommodate your new plugin.
Conclusion
Although more limited, middleware plugins allow you to control the response flow in a way similar to what can be done with some Sitecore pipelines. Use them to control things such as custom authorization logic, pre-processing of the request before rendering, custom analytics logic, and custom redirects depending on the request.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.