The age of JSS + Headless + NextJS is all about quality of life improvements for developers. Here's a good one for you.
Often, it's helpful to be able to ping the layout service to see what data you're working with. The problem is that the URL is tricky to remember. You could bookmark it, but what about other devs who don't have the bookmark? Let's make it easier for everyone.
_36// This code makes it easier to ping your layout service without having to remember the long URL that is accessible by default.
_36// Add this file to /pages/api/viewpage or wherever you want.
_36// From there, all you need to do is visit /api/viewpage to view the layout service response for your home page
_36// Subpage would be /api/viewpage/someSubPage
_36import type { NextApiRequest, NextApiResponse } from 'next';
_36const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
_36 const apiHost = process.env.SITECORE_API_HOST
_36 ? process.env.SITECORE_API_HOST
_36 : `https://mysite.sc`;
_36 const siteName = process.env.NEXT_PUBLIC_JSS_SITE_NAME
_36 ? process.env.NEXT_PUBLIC_JSS_SITE_NAME
_36 const apiKey = process.env.SITECORE_API_KEY
_36 ? process.env.SITECORE_API_KEY
_36 const { path } = req.query;
_36 const itemPath = path ? path : '/';
_36 if (typeof itemPath === 'string') {
_36 queryStringPath = itemPath;
_36 queryStringPath = itemPath.join('/');
_36 const layoutServiceResponse = await fetch(
_36 `${apiHost}/sitecore/api/layout/render/jss/?sc_lang=en&sc_site=${siteName}&sc_apikey=${apiKey}&item=${queryStringPath}`
_36 res.status(200).json(await layoutServiceResponse.json());
_36export default handler;
The above solution may not work in all cases. The solution above is for a solution that's configured with REST, however content can also be fetched with GraphQL, and that's where everything is headed (Experience Edge / XM Cloud). I haven't looked yet, but the layout service may not be present, or the method by which one could fetch that data may be different.
Another possible (and potentially more future proof) solution is:
_19import type { NextApiRequest, NextApiResponse } from 'next';
_19import { GetServerSidePropsContext, GetStaticPropsContext } from 'next';
_19import { sitecorePagePropsFactory } from 'lib/page-props-factory';
_19const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
_19 const { path } = req.query;
_19 const context: GetServerSidePropsContext | GetStaticPropsContext = {
_19 const props = await sitecorePagePropsFactory.create(context);
_19 res.status(200).json(props.layoutData);
_19export default handler;
layout-service-factory.ts
_27 GraphQLLayoutService,
_27} from '@sitecore-jss/sitecore-jss-nextjs';
_27import config from 'temp/config';
_27export class LayoutServiceFactory {
_27 create(): LayoutService {
_27 return process.env.FETCH_WITH === 'GraphQL'
_27 ? new GraphQLLayoutService({
_27 endpoint: config.graphQLEndpoint,
_27 apiKey: config.sitecoreApiKey,
_27 siteName: config.jssAppName,
_27 : new RestLayoutService({
_27 apiHost: config.sitecoreApiHost,
_27 apiKey: config.sitecoreApiKey,
_27 siteName: config.jssAppName,
_27 // CHANGE THIS FROM 'default' TO 'jss'
_27 configurationName: 'jss',
_27export const layoutServiceFactory = new LayoutServiceFactory();
Note that this endpoint will be PUBLICLY VIEWABLE, so make sure to lock it down somehow, possibly with this or WAF rules.
Pair this solution with a browser extension that formats JSON, and you're off to the races.
-MG