Why do I get a "502 Gateway" error from NextJs app hosted on Firebase for POST requests only?

Viewed 4655

I started to build an API using NextJs framework. I want it to be hosted on Firebase (Hosting and Functions). Everything is working as long as I send only GET requests. When I send a POST request I receive a "502 Bad Gateway" error.

It's very simple to reproduce. You just have to download and deploy the example provided by the team developing NextJs.

  • create a new project on Firebase console

  • install the "with Firebase hosting" example

  • change the project name in the .firebaserc (line 3) file

  • create a folder "api" under the folder "pages"

  • create a file "hello.js" under the folder "api" and add the following snippet

    export default async (req, res) => {
      const {
        body,
        method
      } = req;
    
      console.log("method :>> ", method);
      console.log("body :>> ", body);
    
      switch (method) {
      case "POST":
        res.status(200).end(`Method ${method} supported!`);
        break;
      default:
        res.setHeader("Allow", ["POST"]);
        res.status(405).end(`Method ${method} Not Allowed`);
      }
    };
    
    
  • deploy the app

  • send a GET request to "https://[project-name].web.app/api/hello" and see it works

  • send a POST request to "https://[project-name].web.app/api/hello" and see it does not work

Do you have a the same error as me?

I spent 2 days to read articles, watch videos and try different configurations. You can even update the firebaseFunctions to add a console.log and see the POST request is caught by the Firebase Cloud Function but the NextJs server does not pass it to our API like it does for a GET request. It's out of my skills range...

Below the output you should have. The POST request should be answered with 200 - Method POST is supported!.

enter image description here

1 Answers

This was a real pain to track down, but after poking around myself for a while, I found that the same issue crops up for PUT and PATCH requests. Which suggested that it had something to do with the body of the request. Annoyingly, after finding that out, I stumbled across the thread of Issue #7960, where they found the same problem.

Simply put, the body of the request processed once by https.onRequest() and then nextjsHandle() tries to parse it again. Because the body was handled already, the raw-body module (within nextjsHandle()) waits indefinitely for 'data' events that will never come.

Currently, there isn't a way to turn off the body parsing done by https.onRequest(), so it must be disabled on the next.js end. Unfortunately, there isn't a global off switch for body parsing that can be added in next.config.js and it must be done for each and every API route (the files in pages/api) (which may change if the proposed fix in PR #16169 is added).

To disable body parsing for a given route, you add the following to the route's file

export const config = {
  api: {
    // disables call to body parsing module
    bodyParser: false,
  }
};

However, as mentioned in Issue #7960 by @rscotten, you might also want to use next dev while developing your app, so you need to enable it while using next dev but disable it while deployed. This can be done using

export const config = {
  api: {
    // disables call to body parsing module while deployed
    bodyParser: process.env.NODE_ENV !== 'production',
  }
};

Applying these changes to hello.js gives:

export default async (req, res) => {
  const {
    body,
    method
  } = req;

  console.log("method :>> ", method);
  console.log("body :>> ", body);

  switch (method) {
  case "POST":
    res.status(200).end(`Method ${method} supported!`);
    break;
  default:
    res.setHeader("Allow", ["POST"]);
    res.status(405).end(`Method ${method} Not Allowed`);
  }
};

export const config = {
  api: {
    // disable nextjs's body parser while deployed
    // (as body parsing is handled by `https.onRequest()`),
    // but enable it for local development using `next dev`
    bodyParser: process.env.NODE_ENV !== 'production',
  }
};
Related