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-Typeheaders 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
fetchand 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:
-
Content-Type — the request header that tells the server the media type of the body. Common values:
application/json— a JSON string payloadapplication/x-www-form-urlencoded— the default encoding for HTML form submissionsmultipart/form-data— file uploads (not covered in this lesson)
-
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.bodyisundefined.
Express ships with two built-in parsers:
| Middleware | Parses |
|---|---|
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()andexpress.urlencoded()are registered globally, so every subsequent route benefits automatically.- Both POST handlers read
req.bodyidentically — 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
- Open Postman and create a new request.
- Set the method to POST and the URL to
http://localhost:3000/json. - In the Body tab, choose raw → JSON and paste:
{
"name": "Alice",
"age": 30
}
- Hit Send. Postman shows the
201response echoing your payload, and your terminal logs:
Received JSON data: { name: 'Alice', age: 30 }
Step 2: Test the Form Data POST Endpoint
- Create another request: POST →
http://localhost:3000/form. - In Body, choose x-www-form-urlencoded.
- Add two key-value pairs:
- Key:
name, Value:Bob - Key:
age, Value:25
- Key:
- 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
agecomes back as a string'25'from form data versus a number30from 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 asapplication/x-www-form-urlencodedautomatically.
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
fetchPOST and logs the echoed object in the browser DevTools console. - The native form navigates to
/formand 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.
- Register
express.text()middleware inserver.js:
app.use(express.text());
- 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 });
});
- In Postman, POST to
http://localhost:3000/textwith 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) string — express.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) undefined — express.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-Typeheader tells Express which middleware should parse it. express.json()handlesapplication/jsonbodies;express.urlencoded({ extended: true })handles HTML form submissions.- Both parsers must be registered with
app.use()before the routes that readreq.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
- Express.js Request Body Parsing docs — official reference for
req.bodyand related middleware - MDN — Using Fetch — deep dive on sending POST requests from the browser with
fetch - Postman Learning Center — how to craft POST requests with different body types in Postman
- ⬅️ Previous: File System
- ➡️ Next: CRUD Operations