0

I am doing some troubleshooting with my Sitecore(10.2.1)/NextJS(12.3.1)/JSS(20.0.3)/Docker setup. I am running my backend in Docker and my front end via npm on my host machine.

I am POSTing to http://localhost:3000/api/editing/render but I get an error:

POSTMAN POST to api editing render

{
    "html": "<html><body>Error: Unable to extract editing data from request. Ensure `bodyParser` middleware is enabled on your Next.js API route.</body></html>"
}

I do have bodyParser definitions, so it is not clear what the problem is.

bodyParser definition

3
  • Please add the JSS and Sitecore versions to the question. You can try increasing the bodyParser size limit to say 20mb, and check. Commented Oct 24, 2023 at 10:31
  • Thanks @VinayJadav - the versions have been added. I bumped up the size limit to 20mb, but that made no difference. Commented Oct 24, 2023 at 18:15
  • 1
    You get this error when the request body is invalid. Can you also post the request body you are using here? Commented Oct 25, 2023 at 8:21

1 Answer 1

0

These OOB comments in the render.js/render.ts file provide additional context:

/**
 * This Next.js API route is used to handle POST requests from the Sitecore Experience Editor.
 * This route should match the `serverSideRenderingEngineEndpointUrl` in your Sitecore configuration,
 * which is set to "http://localhost:3000/api/editing/render" by default (see \sitecore\config\JssNextWeb.config).
 *
 * The `EditingRenderMiddleware` will
 *  1. Extract editing data from the Experience Editor POST request
 *  2. Stash this data (for later use in the page render request) via the `EditingDataService`, which returns a key for retrieval
 *  3. Enable Next.js Preview Mode, passing our stashed editing data key as preview data
 *  4. Invoke the actual page render request, passing along the Preview Mode cookies.
 *     This allows retrieval of the editing data in preview context (via the `EditingDataService`) - see `SitecorePagePropsFactory`
 *  5. Return the rendered page HTML to the Sitecore Experience Editor
 */

// Bump body size limit (1mb by default) for Experience Editor payload
// See https://nextjs.org/docs/api-routes/api-middlewares#custom-config

As the error message suggests, editing data is unable to be extracted from the request. Some scenarios in which you may see this error are:

  • bodyParser middleware is not enabled on the route
  • You have hit the sizeLimit defined in your bodyParser config
  • The POST request lacks a request body entirely (undefined) -- this will 100% trigger the error described
  • The POST request body is malformed

In order to troubleshoot, check your Next logs for more error details. You may see something like:

Error: Unable to extract editing data from request. Ensure `bodyParser` middleware is enabled on your Next.js API route.
    at extractEditingData (C:\projects\MySite\node_modules\@sitecore-jss\sitecore-jss-nextjs\dist\cjs\middleware\editing-render-middleware.js:174:15)
    at EditingRenderMiddleware.<anonymous> (C:\projects\MySite\node_modules\@sitecore-jss\sitecore-jss-nextjs\dist\cjs\middleware\editing-render-middleware.js:54:37)
    at Generator.next (<anonymous>)
    at C:\projects\MySite\node_modules\@sitecore-jss\sitecore-jss-nextjs\dist\cjs\middleware\editing-render-middleware.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (C:\projects\MySite\node_modules\@sitecore-jss\sitecore-jss-nextjs\dist\cjs\middleware\editing-render-middleware.js:4:12)
    at EditingRenderMiddleware.handler (C:\projects\MySite\node_modules\@sitecore-jss\sitecore-jss-nextjs\dist\cjs\middleware\editing-render-middleware.js:28:38)
    at Object.apiResolver (C:\projects\MySite\node_modules\next\dist\server\api-utils\node.js:366:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async DevServer.runApi (C:\projects\MySite\node_modules\next\dist\server\next-server.js:481:9)

You can also print the request handler to the console like so:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '20mb',
    },
  },
}

// Wire up the EditingRenderMiddleware handler
const handler = new EditingRenderMiddleware().getHandler();

console.log(`***HANDLER*** ${handler}`)

Output:

(req, res) => __awaiter(this, void 0, void 0, function* () {
    var _e;
    const { method, query, body, headers } = req;
    sitecore_jss_1.debug.experienceEditor('editing render middleware start: %o', {
        method,
        query,
        headers,
        body,
    });
    if (method !== 'POST') {
        sitecore_jss_1.debug.experienceEditor('invalid method - sent %s expected POST', method);
        res.setHeader('Allow', 'POST');
        return res.status(405).json({
            html: `<html><body>Invalid request method '${method}'</body></html>`,
        });
    }
    // Validate secret
    const secret = (_e = query[editing_data_service_2.QUERY_PARAM_EDITING_SECRET]) !== null && _e !== void 0 ? 
_e : body === null || body === void 0 ? void 0 : body.jssEditingSecret;
    if (secret !== utils_1.getJssEditingSecret()) {
        sitecore_jss_1.debug.experienceEditor('invalid editing secret - sent "%s" expected "%s"', secret, utils_1.getJssEditingSecret());
        return res.status(401).json({
            html: '<html><body>Missing or invalid secret</body></html>',
        });
    }
    try {
        // Extract data from EE payload
        const editingData = extractEditingData(req);
        // Resolve server URL
        const serverUrl = this.resolveServerUrl(req);
        // Stash for use later on (i.e. within getStatic/ServerSideProps).
        // This ultimately gets stored on disk (using our EditingDataDiskCache) for compatibility with Vercel Serverless Functions.
        // Note we can't set this directly in setPreviewData since it's stored as a cookie (2KB limit)
        // https://nextjs.org/docs/advanced-features/preview-mode#previewdata-size-limits)
        const previewData = yield this.editingDataService.setEditingData(editingData, serverUrl);
        // Enable Next.js Preview Mode, passing our preview data (i.e. editingData cache key)
        res.setPreviewData(previewData);
        // Grab the Next.js preview cookies to send on to the render request
        const cookies = res.getHeader('Set-Cookie');
        // Make actual render request for page route, passing on preview cookies.
        // Note timestamp effectively disables caching the request in Axios (no amount of cache headers seemed 
to do it)
        const requestUrl = this.resolvePageUrl(serverUrl, editingData.path);
        sitecore_jss_1.debug.experienceEditor('fetching page route for %s', editingData.path);
        const pageRes = yield this.dataFetcher
            .get(`${requestUrl}?timestamp=${Date.now()}`, {
            headers: {
                Cookie: cookies.join(';'),
            },
        })
            .catch((err) => {
            // We need to handle not found error provided by Vercel
            // for `fallback: false` pages
            if (err.response.status === 404) {
                return err.response;
            }
            throw err;
        });
        let html = pageRes.data;
        if (!html || html.length === 0) {
            throw new Error(`Failed to render html for ${requestUrl}`);
        }
        // replace phkey attribute with key attribute so that newly added renderings
        // show correct placeholders, so save and refresh won't be needed after adding each rendering
        html = html.replace(new RegExp('phkey', 'g'), 'key');
        // When SSG, Next will attempt to perform a router.replace on the client-side to inject the query string parms
        // to the router state. See https://github.com/vercel/next.js/blob/v10.0.3/packages/next/client/index.tsx#L169.
        // However, this doesn't really work since at this point we're in the editor and the location.search has nothing
        // to do with the Next route/page we've rendered. Beyond the extraneous request, this can result in a 404 with
        // certain route configurations (e.g. multiple catch-all routes).
        // The following line will trick it into thinking we're SSR, thus avoiding any router.replace.
        html = html.replace(constants_1.STATIC_PROPS_ID, constants_1.SERVER_PROPS_ID);
        const body = { html };
        // Return expected JSON result
        sitecore_jss_1.debug.experienceEditor('editing render middleware end: %o', { status: 200, body });     
        res.status(200).json(body);
    }
    catch (error) {
        console.error(error);
        if (error.response || error.request) {
            // Axios error, which could mean the server or page URL isn't quite right, so provide a more helpful hint
            console.info(
            // eslint-disable-next-line quotes
            "Hint: for non-standard server or Next.js route configurations, you may need to override the 'resolveServerUrl' or 'resolvePageUrl' available on the 'EditingRenderMiddleware' config.");
        }
        res.status(500).json({
            html: `<html><body>${error}</body></html>`,
        });
    }
})

In summary, it looks like you are hitting this in the handler as a result of an error during the call to extractEditingData():

res.status(500).json({
  html: `<html><body>${error}</body></html>`,
);

Your specific issue may vary, so use the above as a guide to investigate further.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.