Sitecore Headless Next.js – Runtime rewrites

When you create a Sitecore Next.js project you will notice there is a configuration file generated, called next.config.js. This config holds different Sitecore related config entries, but in this post I would like to focus on the async rewrites() method. This is a Next.js feature, to handle rewrites in the app. Rewrites can be useful for several things. By default Sitecore uses rewrites to proxy the media and some Sitecore API requests.

async rewrites() {
// When in connected mode we want to proxy Sitecore paths off to Sitecore
return [
// media items
{
source: '/-/:path*',
destination: `${jssConfig.sitecoreApiHost}/-/:path*`,
}
];
}
view raw next.config.js hosted with ❤ by GitHub

You need these rewrites when your Sitecore CD servers in private network and not exposed publicly to serve media files. To use these rewrites are good enough until you develop locally or you rebuild the application for each stage (e.g. DEV, QA, UAT, PROD).

Problem

The problem is not related to Sitecore at all. After few hours of troubleshooting with Benjamin Gyuro we found how Next.js rewrites configurations are working. Here is the GitHub issue which explains it in detail. The problem in a nutshell, that the rewrites needs to be generated in build time (in the routes-manifest.json) instead of runtime, therefore we can’t use environment variables which differs on each stage. If you use Docker or Kubernetes you don’t want to build different Docker images just because a variable is different on each environment.

Solution

I would like to point out, this solution only works/tested for media items. The idea was to implement an API in the Next.js app which then requests for the media item from Sitecore and responding with content served by the Sitecore CD server. For this I used axios with forwarding the request headers. The following index.ts should sit in the /api/rewrite-sitecore-media folder in my example.

import axios from 'axios';
import { NextApiRequest, NextApiResponse } from 'next';
/**
* Custom API implementation for rewrites to support runtime rewrite config
* https://github.com/vercel/next.js/issues/21888
*/
export const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
const newPath = req.url as string;
if (newPath) {
const url = `${process.env.SITECORE_API_HOST}${newPath}`;
const { data, status, headers } = await axios({
url,
responseType: 'arraybuffer',
});
// map headers
Object.entries(headers).map((h) => res.setHeader(h[0], (h[1] as string) ?? ''));
res.status(status).send(data);
} else {
res.status(400).json({ error: 'Request URL was not provided.' });
}
};
export default handler;
view raw index.ts hosted with ❤ by GitHub

Then the rewrites should be adapted a bit, like the following:

async rewrites() {
// When in connected mode we want to proxy Sitecore paths off to Sitecore
return [
// media items
{
source: '/-/:path*',
destination: '/api/rewrite-sitecore-media',
}
];
}
view raw next.config.js hosted with ❤ by GitHub

You probably notice, that the destination does not contain the :path* token anymore. This is because it’s a rewrite, which means there is no extra request called so I can just use the req.url in the API implementation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s