This module covers how to receive and process form data in Node.js with Express. We'll compare the approach with PHP's superglobals and cover security considerations.
Run: npm install then node form-demo.js
Try it live: cse135.site:3003/demo - Test GET, POST, and JSON submissions
In PHP, form data magically appears in superglobals. In Node.js/Express, you must explicitly configure middleware to parse it.
| Scenario | PHP | Express |
|---|---|---|
GET parameters?name=Alice |
$_GET['name'] |
req.query.name |
| POST form data | $_POST['name'] |
req.body.name(requires middleware) |
| JSON body | json_decode(file_get_contents('php://input')) |
req.body(requires middleware) |
GET data is available immediately in Express via req.query:
// URL: /search?q=nodejs&page=2
app.get('/search', (req, res) => {
const query = req.query.q; // 'nodejs'
const page = req.query.page; // '2' (always a string!)
res.json({
query: query,
page: parseInt(page) || 1
});
});
parseInt() or Number() to convert to numbers.
Traditional HTML forms submit data as application/x-www-form-urlencoded. Express needs middleware to parse this:
const express = require('express');
const app = express();
// Enable parsing of URL-encoded form data
app.use(express.urlencoded({ extended: true }));
app.post('/login', (req, res) => {
const username = req.body.username;
const password = req.body.password;
// Validate and process...
res.json({ message: `Hello, ${username}!` });
});
The HTML form:
<form action="/login" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<button type="submit">Login</button>
</form>
Many APIs accept JSON instead of form-encoded data. Add the JSON middleware:
// Enable parsing of JSON bodies
app.use(express.json());
app.post('/api/users', (req, res) => {
const userData = req.body;
console.log(userData); // { name: 'Alice', email: 'alice@example.com' }
res.status(201).json({
message: 'User created',
user: userData
});
});
Client-side JavaScript:
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
});
By enabling both middleware, your endpoints can accept either format. This supports progressive enhancement: the form works without JavaScript, but JavaScript clients can send JSON for richer interactions.
const express = require('express');
const app = express();
// Middleware for both types of requests
app.use(express.json()); // JSON bodies
app.use(express.urlencoded({ extended: true })); // Form bodies
app.use(express.static('public')); // Static files
// Now req.body works for both JSON and form data!
// The same endpoint can handle:
// - Traditional form submission (no JS required)
// - fetch() with JSON (JS-enhanced experience)
// form-demo.js
const express = require('express');
const app = express();
const PORT = 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
// Handle GET request (display search results)
app.get('/search', (req, res) => {
const query = req.query.q || '';
res.json({
type: 'GET',
searchQuery: query,
message: query ? `Searching for: ${query}` : 'No query provided'
});
});
// Handle POST request (form submission)
app.post('/submit', (req, res) => {
const { username, email, message } = req.body;
// Basic validation
if (!username || !email) {
return res.status(400).json({
error: 'Username and email are required'
});
}
res.json({
type: 'POST',
received: { username, email, message },
success: true
});
});
// Handle JSON API request
app.post('/api/data', (req, res) => {
console.log('Received JSON:', req.body);
res.json({
type: 'JSON API',
received: req.body,
timestamp: new Date().toISOString()
});
});
app.listen(PORT, () => {
console.log(`Form demo running at http://localhost:${PORT}`);
console.log('Open http://localhost:${PORT}/form.html to test');
});
Never trust user input! Always validate and sanitize:
app.post('/register', (req, res) => {
const { username, email, age } = req.body;
// Type checking
if (typeof username !== 'string') {
return res.status(400).json({ error: 'Invalid username' });
}
// Length validation
if (username.length < 3 || username.length > 50) {
return res.status(400).json({ error: 'Username must be 3-50 characters' });
}
// Email format (basic check)
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Invalid email' });
}
// Number conversion and range
const ageNum = parseInt(age);
if (isNaN(ageNum) || ageNum < 0 || ageNum > 150) {
return res.status(400).json({ error: 'Invalid age' });
}
// All valid!
res.json({ success: true, username, email, age: ageNum });
});
express-validator or joi instead of manual validation.
When displaying user input in HTML, always escape it to prevent Cross-Site Scripting (XSS) attacks:
// DANGEROUS - Don't do this!
app.get('/greet', (req, res) => {
res.send(`<h1>Hello, ${req.query.name}!</h1>`);
});
// Attack: /greet?name=<script>alert('hacked')</script>
// SAFE - Escape HTML entities
function escapeHtml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
app.get('/greet', (req, res) => {
const safeName = escapeHtml(req.query.name || 'Guest');
res.send(`<h1>Hello, ${safeName}!</h1>`);
});
htmlspecialchars() escapes output. Node.js has no built-in equivalent; you must use a library or function like above. Template engines like EJS or Handlebars escape by default.
Test form handling with our live demo server:
This interactive demo lets you:
req.query in actionreq.bodyView the source: form-live-demo.js
| Task | Code |
|---|---|
| Enable form parsing | app.use(express.urlencoded({ extended: true })) |
| Enable JSON parsing | app.use(express.json()) |
| Get query params | req.query.paramName |
| Get POST/JSON body | req.body.fieldName |
| Validate input | Check type, length, format before using |
| Prevent XSS | Escape HTML entities or use a template engine |