Form Handling

Receiving and processing form data in Node.js with Express, comparing PHP superglobals to Express middleware

PHP vs Node.js: Form Data Access

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 Parameters (Query Strings)

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 }); });

POST Form Data (URL-Encoded)

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:

JSON Body (API Requests)

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' }) });

Supporting Both Methods (Progressive Enhancement)

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)

Complete Form Handling Example

// 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'); });

Input Validation

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 }); });

XSS Prevention

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(`

Hello, ${req.query.name}!

`); }); // Attack: /greet?name= // SAFE - Escape HTML entities function escapeHtml(text) { return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } app.get('/greet', (req, res) => { const safeName = escapeHtml(req.query.name || 'Guest'); res.send(`

Hello, ${safeName}!

`); });

Summary

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