Handling POST Requests in Node.js
Tags: node, beginners
Published: 2020-09-05 21:17:04

Last week I covered simple routing with Node.js. If you haven't already read the article, you can find it [here](https://dev.to/brandonmweaver/introduction-to-node-js-6i1). This week I wanted to expand on the introduction, and I believe that the next step is handling POST requests, so let's get started!

Take a moment to set up your file structure.

```
controllers
    app_controller.js
    items_controller.js
views
    items
        index.html
        new.html
    index.html
app.js
```

Now, place the following markup in your top-level index.html.

```html
<!DOCTYPE html>
<html>
    <head>
        <title>Index</title>
    </head>
    <body>
        <h1>Index</h1>
        <a href="/items">Items</a>
    </body>
</html>
```

Place the following markup in items/index.html.

```html
<!DOCTYPE html>
<html>
    <head>
        <title>Items</title>
    </head>
    <body>
        <h1>Items</h1>
        <a href="/">Index</a>
        <a href="/items/new">Create Item</a>
    </body>
</html>
```

For our final template, we're going to create a form which will serve as our means to submit a POST request. Place the following markup in items/new.html, and be sure that the form's `action` field is set to `"/items"` and its `method` field is set to `"POST"`. It's also worth mentioning that the value of the `name` field of our text input will be the name of the parameter which we extract data from when we handle the request.

```html
<!DOCTYPE html>
<html>
    <head>
        <title>Create Item</title>
    </head>
    <body>
        <h1>Create Item</h1>
        <a href="/items">Items</a>
        <form action="/items" method="POST">
            <input type="text" name="value" />
            <input type="submit" />
        </form>
    </body>
</html>
```

With our views in place, let's focus on building our controllers. We'll start with a simple application controller. Place the following code in app_controller.js.

```javascript
const fs = require("fs");

const appController = (request, response) => {
    if (request.url === '/') {
        fs.readFile("./views/index.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            return response.end();
        });
    }
}

module.exports = appController;
```

We're going to separate the logic for our items from our application controller to keep things tidy. To do that, we'll first place the following code in items_controller.js

```javascript
const fs = require("fs");

const items = [];

const itemsController = (request, response) => {
    if (request.url === "/items") {
        fs.readFile("./views/items/index.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            response.write("<ul>");
            for (const item of items) {
                response.write(`<li>${item}</li>`);
            }
            response.write("</ul>");
            return response.end();
        });
    }
    if (request.url === "/items/new") {
        fs.readFile("./views/items/new.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            return response.end();
        });
    }
}

module.exports = itemsController;
```

You may be wondering about the block of code dedicated to our items route. For this example, we're going to render our items in an unordered list. We need to generate the list dynamically to account for changes made to the items array. We're simply making multiple calls to `response.write` in order to append the elements manually.

Now, Let's integrate our items controller. To do this, we simply call it from within the application controller, being sure to pass the appropriate arguments down.

app_controller.js should now look as follows.

```javascript
const fs = require("fs");

const itemsController = require("./items_controller");

const appController = (request, response) => {
    if (request.url === '/') {
        fs.readFile("./views/index.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            return response.end();
        });
    }
    itemsController(request, response);
}

module.exports = appController;
```

Before we worry about implementing the logic for our POST request, we need to create our server by placing the following code in app.js.

```javascript
const http = require("http");
const controller = require("./controllers/app_controller");
http.createServer(controller).listen(3000);
```

At this point, it would probably be worthwhile to run the following command in your terminal.

```
node app.js
```

Visit `localhost:3000` in your browser, and confirm that you are able to navigate to each route. You can also take this opportunity to test the form submission. You'll notice that upon submission, we're directed to the /items route, however, our entry doesn't appear on the document. Let's get that sorted out!

To implement this behavior, we only need to alter our /items route, and leverage a Node.js package called `querystring` to parse the incoming parameter.

We need to start by creating two separate execution paths within the route. We will check the `method` property of our `request` parameter to create these branches.

The overall layout of our route will look as follows.

```javascript
if (request.url === "/items") {
    if (request.method === "POST") {
        ...
    }
    else {
        fs.readFile("./views/items/index.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            response.write("<ul>");
            for (const item of items) {
                response.write(`<li>${item}</li>`);
            }
            response.write("</ul>");
            return response.end();
        });
    }
}
```

As you can see, our GET request logic will now only execute in the event that the `method` property of the `request` parameter is not equal to `"POST"`.

Now, for the somewhat tricky part. Due to the asynchronicity of HTTP requests, we'll need to utilize event-driven behavior to capture the incoming data, and push the value into our array using callbacks.

When we're finished implementing this feature, items_controller.js will look as follows.

```javascript
const fs = require("fs");
const qs = require("querystring");

const items = [];

const itemsController = (request, response) => {
    if (request.url === "/items") {
        if (request.method === "POST") {
            let body = '';
            request.on("data", chunk => {
                body = `${body}${chunk.toString()}`;
            });
            request.on("end", () => items.push(qs.parse(body).value));
            response.writeHead(301, { Location: "/items" });
            return response.end();
        }
        else {
            fs.readFile("./views/items/index.html", (error, html) => {
                if (error) throw error;
                response.write(html);
                response.write("<ul>");
                for (const item of items) {
                    response.write(`<li>${item}</li>`);
                }
                response.write("</ul>");
                return response.end();
            });
        }
    }
    if (request.url === "/items/new") {
        fs.readFile("./views/items/new.html", (error, html) => {
            if (error) throw error;
            response.write(html);
            return response.end();
        });
    }
}

module.exports = itemsController;
```

At a quick glance, you'll notice two calls to an unfamiliar method of the `request` parameter named `on`. The first argument we pass to this method is the event we're listening for. When the event is triggered, our callback will be invoked.

When the `"data"` event is triggered, we're passing a parameter named `chunk` into an anonymous function which serves as our callback. Within this function we're concatenating the stringified chunk of data to an empty string we've named `body`. This data contains the value we want to extract, however, we need to gather the data in chunks as they come through.

The `"end"` event is triggered once the request is finalized. This is the perfect time to parse our `body` variable, and store the `value` property of the newly returned object in our items array, as we can be sure we have all of the data required at this time. Finally, we write a status code of `301`, and a location of `"/items"` to the head of our response. This will invoke a redirect to /items once we call `response.end`, which follows immediately.

Once again, enter the following command in your terminal.

```
node app.js
```

Visit `localhost:3000/items/new` in your browser, fill out the text field and press submit. You should now see your entry displayed in the unordered list! Note that you are able to submit multiple entries, although, once you close the application, the entries will be lost, as we aren't persisting the data.

I hope some of you were able to find this small tutorial helpful. Next week I will cover data persistence with a SQLite database, and the `sqlite3` Node.js package. I will take that opportunity to discuss rendering JSON for API setups as well. We will begin that endeavor where we left off here.