Topic 16 of 18 · Node Expert

Topic 6 : Reading POST data

Lesson TL;DRTopic 6: Reading POST Data 📖 6 min read · 🎯 intermediate · 🧭 Prerequisites: jsondata, filesystem Why this matters Up until now, our Express server has only been sending data out — responding to GET...
6 min read·intermediate·express · post-requests · middleware · body-parser

Topic 6: Reading POST Data

📖 6 min read · 🎯 intermediate · 🧭 Prerequisites: json-data, file-system

Why this matters

Up until now, our Express server has only been sending data out — responding to GET requests, serving pages. But here's the thing — most real apps need to receive data too. A login form, a signup page, a search bar — all of those send a POST request with data attached. Without the right setup, Express quietly ignores that data entirely. Today we wire up express.json() and handle both JSON and URL-encoded bodies, so our server can actually read what the user is sending.

What You'll Learn

  • What POST requests are and why Content-Type headers matter
  • How to configure Express middleware to parse JSON and form-encoded bodies
  • How to write POST endpoints that read req.body
  • How to test POST endpoints with Postman
  • How to build and serve an HTML form that submits POST data via fetch and native form submission

The Analogy

Think of your Express server as a package-sorting facility. When a delivery truck (the HTTP client) pulls up, it drops off a box (the POST request). The box has a label on it — that's the Content-Type header — that says whether it contains loose JSON pellets or compressed form-encoded foam peanuts. Without the right sorting machine (body-parser middleware), your workers open the box and find a raw, unintelligible stream of bytes. Plug in express.json() and express.urlencoded() and suddenly the machine reads the label, unpacks the contents, and hands your route handler a clean JavaScript object — no guesswork required.

Chapter 1: Introduction to POST Requests

POST requests are the standard HTTP method for sending data to a server to create or update a resource. Unlike GET requests — which pass data in the URL — POST requests carry a body that can hold structured payloads.

Two concepts are central here:

  1. Content-Type — the request header that tells the server the media type of the body. Common values:

    • application/json — a JSON string payload
    • application/x-www-form-urlencoded — the default encoding for HTML form submissions
    • multipart/form-data — file uploads (not covered in this lesson)
  2. Body Parser Middleware — Express middleware that reads the raw bytes of the request body and transforms them into a JavaScript object available on req.body. Without it, req.body is undefined.

Express ships with two built-in parsers:

MiddlewareParses
express.json()application/json bodies
express.urlencoded({ extended: true })application/x-www-form-urlencoded bodies

Both must be registered with app.use() before any route handlers that read req.body.

Chapter 2: Setting Up Express to Handle POST Requests

Step 1: Set Up the Project

Create a fresh directory, initialize a Node project, and install Express.

mkdir post-data-demo
cd post-data-demo
npm init -y
npm install express

Step 2: Create the Server File

Create server.js at the project root.

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware to parse JSON and URL-encoded data
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
    res.send('Hello, Vizag!');
});

// POST endpoint to handle JSON data
app.post('/json', (req, res) => {
    const data = req.body;
    console.log('Received JSON data:', data);
    res.status(201).send(data);
});

// POST endpoint to handle form data
app.post('/form', (req, res) => {
    const data = req.body;
    console.log('Received form data:', data);
    res.status(201).send(data);
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Key decisions in this file:

  • express.json() and express.urlencoded() are registered globally, so every subsequent route benefits automatically.
  • Both POST handlers read req.body identically — the middleware did the heavy lifting of parsing the format.
  • res.status(201) signals "resource created" — semantically correct for a successful POST.

Step 3: Run the Server

node server.js

The API is now running at http://localhost:3000. Use Postman or an HTML form to fire POST requests at it.

Chapter 3: Testing POST Endpoints with Postman

the trainer walked the class through Postman, the go-to GUI for manual API testing.

Step 1: Test the JSON POST Endpoint

  1. Open Postman and create a new request.
  2. Set the method to POST and the URL to http://localhost:3000/json.
  3. In the Body tab, choose rawJSON and paste:
{
    "name": "Alice",
    "age": 30
}
  1. Hit Send. Postman shows the 201 response echoing your payload, and your terminal logs:
Received JSON data: { name: 'Alice', age: 30 }

Step 2: Test the Form Data POST Endpoint

  1. Create another request: POSThttp://localhost:3000/form.
  2. In Body, choose x-www-form-urlencoded.
  3. Add two key-value pairs:
    • Key: name, Value: Bob
    • Key: age, Value: 25
  4. Hit Send. The response echoes { name: 'Bob', age: '25' } (note: form values are always strings until you cast them).
Received form data: { name: 'Bob', age: '25' }

Notice that age comes back as a string '25' from form data versus a number 30 from JSON. express.json() preserves types; express.urlencoded() does not — keep that in mind when validating inputs.

Chapter 4: Creating a Web Form to Submit POST Data

Postman is great for testing, but real users interact through browser forms. The class built an HTML front-end to drive both submission paths.

Step 1: Create an HTML Form

Create a public/ directory and add index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Submit Data</title>
</head>
<body>
    <h1>Submit JSON Data</h1>
    <form id="jsonForm">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <label for="age">Age:</label>
        <input type="number" id="age" name="age">
        <button type="button" onclick="submitJson()">Submit JSON</button>
    </form>

    <h1>Submit Form Data</h1>
    <form action="/form" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <label for="age">Age:</label>
        <input type="number" id="age" name="age">
        <button type="submit">Submit Form</button>
    </form>

    <script>
        function submitJson() {
            const name = document.getElementById('name').value;
            const age = document.getElementById('age').value;
            fetch('/json', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ name, age })
            })
            .then(response => response.json())
            .then(data => console.log('Success:', data))
            .catch(error => console.error('Error:', error));
        }
    </script>
</body>
</html>

Two submission paths in one page:

  • JSON form — uses the Fetch API to manually serialize inputs as JSON and sets Content-Type: application/json. The page does not navigate away; results appear in the console.
  • Form data form — uses a native <form action="/form" method="post"> which the browser submits as application/x-www-form-urlencoded automatically.

Step 2: Serve the HTML Form

Update server.js to serve the public/ directory as static files:

const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware to parse JSON and URL-encoded data
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Serve static files from the public directory
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// POST endpoint to handle JSON data
app.post('/json', (req, res) => {
    const data = req.body;
    console.log('Received JSON data:', data);
    res.status(201).send(data);
});

// POST endpoint to handle form data
app.post('/form', (req, res) => {
    const data = req.body;
    console.log('Received form data:', data);
    res.status(201).send(data);
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

express.static() serves everything in public/ automatically, and res.sendFile() handles the explicit root route.

Step 3: Run the Server and Test the Forms

node server.js

Navigate to http://localhost:3000. Fill out either form and submit:

  • The JSON form fires a fetch POST and logs the echoed object in the browser DevTools console.
  • The native form navigates to /form and the browser displays the echoed JSON response directly.

Both paths hit your Express handlers; both log to the terminal.

sequenceDiagram
    participant Browser
    participant Express
    Browser->>Express: POST /json (Content-Type: application/json)
    Express->>Express: express.json() parses body → req.body
    Express-->>Browser: 201 { name, age }

    Browser->>Express: POST /form (Content-Type: application/x-www-form-urlencoded)
    Express->>Express: express.urlencoded() parses body → req.body
    Express-->>Browser: 201 { name, age }

🧪 Try It Yourself

Task: Add a third POST endpoint /text that accepts a plain-text body and echoes it back with a word count.

  1. Register express.text() middleware in server.js:
app.use(express.text());
  1. Add the endpoint:
app.post('/text', (req, res) => {
    const body = req.body;
    const wordCount = body.trim().split(/\s+/).length;
    res.status(201).json({ body, wordCount });
});
  1. In Postman, POST to http://localhost:3000/text with Body → raw → Text set to:
The class of Vizag assembles

Success criterion: The response JSON should include { body: "The class of Vizag assembles", wordCount: 6 }.

🔍 Checkpoint Quiz

Q1. What does express.urlencoded({ extended: true }) do, and why must it be registered before your route handlers?

Q2. Given this server snippet and the Postman request below, what does console.log(typeof req.body.age) print?

app.use(express.urlencoded({ extended: true }));
app.post('/form', (req, res) => {
    console.log(typeof req.body.age);
    res.send('ok');
});

Postman body (x-www-form-urlencoded): name=Carol&age=42

A) number B) string C) undefined D) object

Q3. A client sends a POST request to /json with Content-Type: text/plain and a JSON-looking body. express.json() is the only body middleware. What is req.body inside the handler?

A) The parsed JavaScript object B) undefined C) The raw string D) An empty object {}

Q4. You want your /api/users POST endpoint to reject requests whose body is missing a name field and return 400 Bad Request. Where should that validation logic live, and how would you implement it?

A1. express.urlencoded() parses request bodies sent with Content-Type: application/x-www-form-urlencoded (the default HTML form encoding) and populates req.body. It must be registered with app.use() before any route handler that reads req.body because Express executes middleware in registration order — a handler that runs before the parser sees req.body as undefined.

A2. B) stringexpress.urlencoded() does not coerce types. All form values arrive as strings, even when the value looks numeric. You must cast explicitly: Number(req.body.age) or parseInt(req.body.age, 10).

A3. B) undefinedexpress.json() only activates when the Content-Type header is exactly application/json. A text/plain body bypasses the parser entirely, leaving req.body unset. You would need express.text() to handle that content type.

A4. The validation lives inside the route handler, right after reading req.body. Example:

app.post('/api/users', (req, res) => {
    const { name } = req.body;
    if (!name) {
        return res.status(400).json({ error: 'name is required' });
    }
    res.status(201).json({ name });
});

Return early with 400 before doing any further processing — never proceed with incomplete data.

🪞 Recap

  • POST requests carry a body; the Content-Type header tells Express which middleware should parse it.
  • express.json() handles application/json bodies; express.urlencoded({ extended: true }) handles HTML form submissions.
  • Both parsers must be registered with app.use() before the routes that read req.body.
  • Form-encoded bodies deliver all values as strings — numeric fields need explicit casting.
  • express.static() + res.sendFile() let you serve an HTML front-end from the same Express app that handles your API routes.

📚 Further Reading

Like this topic? It’s one of 18 in Node Expert.

Block your seat for ₹2,500 and join the next cohort.