Topic 31 of 56 · Full Stack Advanced

Topic 6 : Reading POST data

Lesson TL;DRTopic 6: Reading POST Data 📖 6 min read · 🎯 intermediate · 🧭 Prerequisites: filesystem, jointquerynestedqueryfilteringdata Why this matters Up until now, your Express server has mostly been sending...
6 min read·intermediate·express · post-requests · body-parser · forms

Topic 6: Reading POST Data

📖 6 min read · 🎯 intermediate · 🧭 Prerequisites: file-system, joint-query-nested-query-filtering-data

Why this matters

Up until now, your Express server has mostly been sending data out — responding to GET requests, returning JSON, serving messages. But real applications need to go the other way too. When a user fills in a login form, submits an order, or sends a message — that data travels into your server as a POST request. It arrives as a payload: JSON, form fields, plain text. If you don't know how to read that incoming data in Express, you can't build anything interactive. That's exactly what we're tackling in this lesson.

What You'll Learn

  • Understand what POST requests are and how Content-Type shapes the payload format
  • Wire up express.json() and express.urlencoded() middleware to parse incoming bodies
  • Build POST endpoints that handle both JSON and HTML form submissions
  • Test POST endpoints with Postman and a browser-based HTML form

The Analogy

Think of a POST request like a parcel delivery to a sorting facility. The parcel (the HTTP request) arrives at the loading dock (your Express server), and the label on the box — the Content-Type header — tells the sorters exactly what's inside and how to open it. Without the right sorter assigned to that label, the box just sits there unopened on req.body, which would be undefined. Express middleware acts as those trained sorters: express.json() handles boxes labeled application/json, and express.urlencoded() handles boxes labeled application/x-www-form-urlencoded. Once the right sorter processes the parcel, your route handler finds everything neatly unpacked and ready to use.

Chapter 1: Introduction to POST Requests

POST requests let clients send data to the server to create or update a resource. Unlike GET requests, they carry a body — a payload that can arrive in several formats.

Key concepts:

  1. Content-Type — The HTTP header that specifies the media type of the request body. Common values:

    • application/json — data encoded as a JSON string
    • application/x-www-form-urlencoded — data encoded as key=value pairs (classic HTML form default)
    • multipart/form-data — used for file uploads and rich form submissions
  2. Body Parser Middleware — Express ships with built-in middleware that reads the raw request stream and populates req.body based on the Content-Type:

    • express.json() — parses application/json payloads
    • express.urlencoded({ extended: true }) — parses URL-encoded form payloads

Without registering these middleware functions, req.body is always undefined inside your route handlers.

Chapter 2: Setting Up Express to Handle POST Requests

Step 1: Set Up the Project

Create a fresh directory, initialize a Node.js 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}`);
});

Both /json and /form read from the same req.body property — the middleware takes care of populating it correctly based on what the client sent.

Step 3: Run the Server

node server.js

The API is now live at http://localhost:3000. Use Postman or a web form to fire POST requests at it.

sequenceDiagram
    participant Client
    participant Middleware as Express Middleware
    participant Route as Route Handler

    Client->>Middleware: POST /json  Content-Type: application/json
    Middleware->>Middleware: express.json() parses body
    Middleware->>Route: req.body = { name: "Alice", age: 30 }
    Route-->>Client: 201 { name: "Alice", age: 30 }

    Client->>Middleware: POST /form  Content-Type: application/x-www-form-urlencoded
    Middleware->>Middleware: express.urlencoded() parses body
    Middleware->>Route: req.body = { name: "Bob", age: "25" }
    Route-->>Client: 201 { name: "Bob", age: "25" }

Chapter 3: Testing POST Endpoints with Postman

Postman is the fastest way to probe your endpoints without writing any client-side code.

Step 1: Test a JSON POST Request

  1. Open Postman and create a new request.
  2. Set Method to POST.
  3. Set URL to http://localhost:3000/json.
  4. Select the Body tab → choose raw → set the type dropdown to JSON.
  5. Paste the following payload:
{
    "name": "Alice",
    "age": 30
}
  1. Click Send. You should receive a 201 Created response echoing the object back, and see Received JSON data: { name: 'Alice', age: 30 } in your terminal.

Step 2: Test a Form Data POST Request

  1. Create another new request in Postman.
  2. Set Method to POST.
  3. Set URL to http://localhost:3000/form.
  4. Select the Body tab → choose x-www-form-urlencoded.
  5. Add the following key/value pairs:
KeyValue
nameBob
age25
  1. Click Send. You should receive a 201 Created response with the form fields echoed as a JSON object.

Chapter 4: Creating a Web Form to Submit POST Data

Testing with Postman is handy, but real users submit data through browser forms. Here the class builds a proper HTML interface backed by the same Express server.

Step 1: Create the HTML Form

Create the directory public/ and add index.html inside it.

public/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="fname">Name:</label>
        <input type="text" id="fname" name="name">
        <label for="fage">Age:</label>
        <input type="number" id="fage" 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>

The first form uses fetch() with a manually set Content-Type: application/json header, so Express routes it through express.json(). The second form uses a native HTML POST submission, which the browser sends as application/x-www-form-urlencoded, handled by express.urlencoded().

Step 2: Serve the HTML Form

Update server.js to serve the public/ directory as static files and send index.html at the root route.

server.js (updated):

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() automatically serves any file found under public/ — CSS, images, and scripts included. The explicit GET / route is a belt-and-suspenders fallback to make sure index.html loads at the root.

Step 3: Run the Server and Test the Form

node server.js

Open http://localhost:3000 in your browser. Fill in the Submit JSON Data form and click "Submit JSON" — watch the browser console for the success log. Then fill in the Submit Form Data form and click "Submit Form" — the page will reload and display the echoed data. Your terminal will show the parsed body for both submissions.

🧪 Try It Yourself

Task: Extend the Express server with a /register POST endpoint that accepts a JSON body containing username, email, and password. The endpoint should validate that all three fields are present. If any are missing, respond with 400 and a JSON error message. If all are present, log the registration and respond with 201 and a success message.

Success criterion: When you POST the following payload to http://localhost:3000/register via Postman, you see a 201 response with { "message": "Registered successfully", "user": "alice" }. When you post a body missing email, you see a 400 response with { "error": "Missing required fields" }.

Starter snippet:

app.post('/register', (req, res) => {
    const { username, email, password } = req.body;

    if (!username || !email || !password) {
        return res.status(400).json({ error: 'Missing required fields' });
    }

    console.log(`New registration: ${username} <${email}>`);
    res.status(201).json({ message: 'Registered successfully', user: username });
});

🔍 Checkpoint Quiz

Q1. What is the purpose of express.urlencoded({ extended: true }) middleware?

A) It parses incoming JSON request bodies into req.body
B) It parses URL-encoded form data (like a native HTML form POST) into req.body
C) It encodes outgoing response data as a URL-safe string
D) It validates that all POST requests include a Content-Type header

Q2. Given the server code below, what will req.body contain inside the /submit handler when a client sends POST /submit with Content-Type: application/json and body {"city":"Vizag"}?

const app = require('express')();
app.use(express.urlencoded({ extended: true }));

app.post('/submit', (req, res) => {
    console.log(req.body);
    res.send('ok');
});

A) { city: 'Vizag' }
B) undefined
C) '{"city":"Vizag"}'
D) An empty object {}

Q3. A developer wants to send POST data from the browser to /api/login without a full page reload, setting the Content-Type to application/json. Which browser API should they use, and what header must they explicitly set?

A) XMLHttpRequest — no headers needed, browsers set them automatically
B) fetch() — set Content-Type: application/json in the headers option
C) <form method="post"> — add a hidden <input name="Content-Type">
D) fetch() — no header needed, Express detects JSON bodies automatically

Q4. Look at this route:

app.post('/data', (req, res) => {
    const data = req.body;
    res.status(201).send(data);
});

A client sends POST /data with Content-Type: application/json and body {"score":42}. What HTTP status code does the client receive, and what is in the response body?

A) 200 with {"score":42}
B) 201 with {"score":42}
C) 201 with an empty body
D) 400 because no validation is performed

A1. B — express.urlencoded() reads the raw body of requests whose Content-Type is application/x-www-form-urlencoded (the default encoding for HTML form submissions) and populates req.body with the parsed key-value pairs.

A2. B — undefined. The server only registered express.urlencoded(), not express.json(). Without express.json() middleware, Express doesn't know how to parse application/json bodies, so req.body is never populated.

A3. B — Use fetch() with headers: { 'Content-Type': 'application/json' } and body: JSON.stringify(payload). Express's express.json() middleware reads that header to decide whether to parse the body as JSON.

A4. B — The route calls res.status(201).send(data), so the client receives HTTP 201 Created with {"score":42} in the response body. express.json() (assumed registered) populates req.body before the handler runs.

🪞 Recap

  • POST requests carry a body payload; the Content-Type header tells Express which middleware should parse it.
  • Register app.use(express.json()) to handle application/json bodies and app.use(express.urlencoded({ extended: true })) to handle HTML form submissions.
  • Access the parsed payload in any route handler via req.body after the appropriate middleware is in place.
  • Postman is the fastest tool for manually firing POST requests at your endpoints during development.
  • Serving an HTML form with express.static() lets you test both fetch()-based JSON submissions and native browser form POSTs from the same page.

📚 Further Reading

Like this topic? It’s one of 56 in Full Stack Advanced.

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