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-Typeshapes the payload format - Wire up
express.json()andexpress.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:
-
Content-Type — The HTTP header that specifies the media type of the request body. Common values:
application/json— data encoded as a JSON stringapplication/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
-
Body Parser Middleware — Express ships with built-in middleware that reads the raw request stream and populates
req.bodybased on theContent-Type:express.json()— parsesapplication/jsonpayloadsexpress.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
- Open Postman and create a new request.
- Set Method to
POST. - Set URL to
http://localhost:3000/json. - Select the Body tab → choose raw → set the type dropdown to JSON.
- Paste the following payload:
{
"name": "Alice",
"age": 30
}
- Click Send. You should receive a
201 Createdresponse echoing the object back, and seeReceived JSON data: { name: 'Alice', age: 30 }in your terminal.
Step 2: Test a Form Data POST Request
- Create another new request in Postman.
- Set Method to
POST. - Set URL to
http://localhost:3000/form. - Select the Body tab → choose x-www-form-urlencoded.
- Add the following key/value pairs:
| Key | Value |
|---|---|
| name | Bob |
| age | 25 |
- Click Send. You should receive a
201 Createdresponse 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-Typeheader tells Express which middleware should parse it. - Register
app.use(express.json())to handleapplication/jsonbodies andapp.use(express.urlencoded({ extended: true }))to handle HTML form submissions. - Access the parsed payload in any route handler via
req.bodyafter 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 bothfetch()-based JSON submissions and native browser form POSTs from the same page.
📚 Further Reading
- Express.js —
express.json()docs — the source of truth on JSON body parsing options - Express.js —
express.urlencoded()docs — options including theextendedflag explained - MDN — Using Fetch — complete guide to the browser
fetch()API for programmatic POST requests - Postman Learning Center — official guide to sending and inspecting HTTP requests with Postman
- ⬅️ Previous: Joint Query, Nested Query & Filtering Data
- ➡️ Next: Basics: Picker, Status Bar & Async Storage