HTTP & Web Servers
Create HTTP servers, parse requests, send responses, and implement routing without frameworks.
Creating HTTP Servers
The http module creates servers with createServer. The callback receives IncomingMessage req and ServerResponse res. Call res.end to finish responses.
Understanding raw HTTP helps debug framework behavior and build lightweight health check endpoints. For production APIs, frameworks add routing, parsing, and middleware.
Use HTTPS with tls.createServer and certificates in production. Terminate TLS at a load balancer or use Node TLS directly depending on infrastructure.
- Set Content-Type on every response body
- Handle req.method and req.url for minimal routing
- Close servers gracefully on SIGTERM
import { createServer } from 'node:http';
createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello\n');
}).listen(3000);Request Parsing
req.url includes path and query string. Parse with new URL(req.url, `http://${req.headers.host}`) for pathname and searchParams.
POST bodies arrive as streams. Accumulate chunks or pipe to parsers. Enforce size limits to prevent memory exhaustion attacks.
Header names are lowercased in Node. Access authorization, content-type, and custom headers from req.headers object.
- Validate Content-Type before JSON.parse on bodies
- Use dedicated middleware for body parsing in Express
- Reject unsupported methods with 405 and Allow header
const url = new URL(req.url!, `http://${req.headers.host}`);
if (url.pathname === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
}Routing and Status Codes
Map method + path pairs to handler functions. Return 404 for unknown routes, 405 for wrong methods, 400 for malformed input, 401/403 for auth failures, 500 for unexpected server errors.
Consistent JSON error shapes help clients: { error: { code, message } }. Include correlation IDs in headers for tracing.
Keep handlers thin; delegate to services that are unit-testable without HTTP mocks when possible.
- Use semantic status codes, not 200 with error payloads
- Implement HEAD and OPTIONS where caches or CORS require them
- Log 5xx with stack traces; log 4xx at info level
const routes = {
'GET /users': listUsers,
'POST /users': createUser,
};
const key = `${req.method} ${pathname}`;
if (routes[key]) return routes[key](req, res);
res.writeHead(404); res.end();