DEV Community

Cover image for Handling POST requests in Next.js getServerSideProps
Stephan Meijer
Stephan Meijer

Posted on • Edited on • Originally published at meijer.ws

Handling POST requests in Next.js getServerSideProps

The documentation pages of Next.js list getServerSideProps under "data fetching", and for any kind of data mutations, they'll point you to the api routes. I'm here to tell you, that there is another way!

In this article, I'm going to show you how you can post to getServerSideProps, and have your components, data fetching, and data mutation logic all collocated in the same file.

HTML Forms

I'll directly dive in. We'll create a form so we can bring this to a working example. Let's keep it simple. Create a new next app with npx create-next-app, and add a form to your index component.

export default function IndexPage({ name = "person" }) {
  return (
    <form method="post">
      <input name="name" defaultValue={name} />
      <button type="submit">submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Note that I don't set the "action" attribute. When the action attribute is left out, it defaults to the current path. And that's exactly what we want it to do. If you'd be using an hosted form service like rake.red, you'd be setting it to an absolute url.

As we'll be adding getServerSideProps in the next step, I've also already provided an initial name value via the props, and default it to person for now.

That's it for the component part. This will render a form that accepts a name, and will be submitted to the current path. Give it a spin!

Server Side Props

So let's start with the basic variant of getServerSideProps.

export async function getServerSideProps({ req }) {
  console.log(req.method, req.body);

  return {
    props: {
      name: "smeijer"
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

I've added an initial value for name, which you'll now see rendered in your form. The value no longer comes from the component, but it's consumed from an "backend part". See it as a query if you will, but without additional request, and without a need for react-query, swr, or other forms of client state management.

Now, if you refresh the page, and resubmit the form, you'll notice that the server will log GET on refresh, and POST on form submit. That's right! We don't need anything special to receive the post. Next already does that out of the box.

But here's the thing. You can add logging statements for req.body, req.params and req.query, but they're all empty. Next.js does not process our request body. So that's something we're gonna fix.

Hooking up body-parser

We'll be using body-parser to read the request body. Install the dependency, and add the following 3 lines to the top of your file:

import bodyParser from "body-parser";
import util from "util";

const getBody = util.promisify(bodyParser.urlencoded());
Enter fullscreen mode Exit fullscreen mode

urlencoded() returns an middleware function that has the common 3 arguments, request, response and next. We use promisify to turn the callback function into an async function, because we won't be using this as a traditional middleware.

If you'll now update getServerSideProps and add await getBody(req, res) to just before that log statement, you'll see that our request body has been processed. 🤯

export async function getServerSideProps({ req, res }) {
  await getBody(req, res);
  console.log(req.method, req.body); // POST { name: 'smeijer' }
  // …
Enter fullscreen mode Exit fullscreen mode

And now you're able to use getServerSideProps to handle your mutations. Seriously, do with the data in req.body, whatever you like. You can access secrets from process.env, and connect to the database in getServerSideProps just fine. It only runs on the server, and won't be exposed to the client.

Full Example

Here is the full, runnable, Next.js page file. You'll notice that I've wrapped the body parser statement inside a check against the request method. That's technically not required, but let's just add the body parsing in there so it doesn't do unnecessary work during GET requests. Besides, we're likely to add more logic in the POST handler, like writing stuff to our database.

Depending on your needs, you can return completely different props, or a similar shape. To keep things predictable, I definitely recommend the latter.

import bodyParser from "body-parser";
import { promisify } from "util";

const getBody = promisify(bodyParser.urlencoded());

export async function getServerSideProps({ req, res }) {
  if (req.method === "POST") {
    await getBody(req, res);
  }

  return {
    props: {
      name: req.body?.name || "smeijer",
      message: req.body ? "received!" : "",
    }
  };
}

export default function IndexPage(props) {
  return (
    <>
      <form method="post">
        <input name="name" defaultValue={props.name} />
        <button type="submit">submit</button>
      </form>
      <p>
        {props.message}
      </p>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Final word

Even though Next.js doesn't advertise the getServerSideProps handler in this way, I think it can be very useful as a simple way to add forms to your page, without the need to define api routes, and deal with client state.

I also haven't tried if this works when hosting on vercel, as I'm running my Next.js apps on my own VPS instead of "serverless". So if anyone can give that a shot, and let us know if it worked out, that would be great.


👋 I'm Stephan, and I'm building metricmouse.com. If you wish to read more of mine, follow me on Twitter.

Top comments (7)

Collapse
 
markjson_4050 profile image
Mark Johnson

Amazing tutorial 👍

Collapse
 
mattellison profile image
mellison

For some reason this didn't work for me locally. Just kept returning null. But using Raw body worked well enough for my case - npmjs.com/package/raw-body which also looks to work on vercel as well.

github.com/vercel/next.js/discussi...

Collapse
 
smeijer profile image
Stephan Meijer

For anyone passing by. I've decided that things should be simplified, and came up with a package. I'll write about it one day, but in the mean time, go check github.com/smeijer/next-runtime

Collapse
 
markjson_4050 profile image
Mark Johnson

Thanks. It worked

Collapse
 
sandbox4013 profile image
sandbox4013

Thanks for tutorial. Currently I'm racking my head how to do same for app router?

Collapse
 
lamine105 profile image
lamine

Very helpful post. Thanks for sharing

Collapse
 
logesh1803 profile image
Logesh

How do I make a form sumbiting without reload user entered data on the form. When the form after submit only show the result but don't clear the input fields.. can you please help me