The HTTP module is fundamental for building web servers in Node.js. Here's how to use it effectively.
Basic HTTP Server#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 res.statusCode = 200;
5 res.setHeader('Content-Type', 'text/plain');
6 res.end('Hello, World!');
7});
8
9server.listen(3000, () => {
10 console.log('Server running at http://localhost:3000/');
11});Request Object#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 // Request properties
5 console.log('Method:', req.method); // GET, POST, etc.
6 console.log('URL:', req.url); // /path?query=value
7 console.log('Headers:', req.headers); // { host: '...', ... }
8 console.log('HTTP Version:', req.httpVersion);
9
10 // Parse URL
11 const url = new URL(req.url, `http://${req.headers.host}`);
12 console.log('Pathname:', url.pathname); // /path
13 console.log('Search:', url.searchParams); // query params
14
15 res.end('OK');
16});
17
18server.listen(3000);Response Object#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 // Set status code
5 res.statusCode = 200;
6 // Or use writeHead
7 res.writeHead(200, {
8 'Content-Type': 'application/json',
9 'X-Custom-Header': 'value'
10 });
11
12 // Set individual headers
13 res.setHeader('Content-Type', 'text/html');
14 res.setHeader('Cache-Control', 'no-cache');
15
16 // Get header
17 const contentType = res.getHeader('Content-Type');
18
19 // Remove header
20 res.removeHeader('X-Custom-Header');
21
22 // Write body (can call multiple times)
23 res.write('Hello ');
24 res.write('World');
25
26 // End response (required)
27 res.end('!');
28
29 // Or end with data
30 res.end(JSON.stringify({ message: 'Hello' }));
31});
32
33server.listen(3000);Routing#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 const url = new URL(req.url, `http://${req.headers.host}`);
5 const path = url.pathname;
6 const method = req.method;
7
8 // Simple routing
9 if (method === 'GET' && path === '/') {
10 res.writeHead(200, { 'Content-Type': 'text/html' });
11 res.end('<h1>Home</h1>');
12 }
13 else if (method === 'GET' && path === '/api/users') {
14 res.writeHead(200, { 'Content-Type': 'application/json' });
15 res.end(JSON.stringify([{ id: 1, name: 'John' }]));
16 }
17 else if (method === 'POST' && path === '/api/users') {
18 // Handle POST
19 let body = '';
20 req.on('data', chunk => body += chunk);
21 req.on('end', () => {
22 const user = JSON.parse(body);
23 res.writeHead(201, { 'Content-Type': 'application/json' });
24 res.end(JSON.stringify({ id: 2, ...user }));
25 });
26 }
27 else {
28 res.writeHead(404, { 'Content-Type': 'text/plain' });
29 res.end('Not Found');
30 }
31});
32
33server.listen(3000);Reading Request Body#
1import { createServer } from 'node:http';
2
3// Promise-based body parser
4async function parseBody(req) {
5 return new Promise((resolve, reject) => {
6 const chunks = [];
7
8 req.on('data', chunk => chunks.push(chunk));
9 req.on('end', () => resolve(Buffer.concat(chunks)));
10 req.on('error', reject);
11 });
12}
13
14// JSON body parser
15async function parseJSON(req) {
16 const body = await parseBody(req);
17 return JSON.parse(body.toString());
18}
19
20const server = createServer(async (req, res) => {
21 if (req.method === 'POST') {
22 try {
23 const data = await parseJSON(req);
24 res.writeHead(200, { 'Content-Type': 'application/json' });
25 res.end(JSON.stringify({ received: data }));
26 } catch (error) {
27 res.writeHead(400, { 'Content-Type': 'application/json' });
28 res.end(JSON.stringify({ error: 'Invalid JSON' }));
29 }
30 } else {
31 res.end('Send a POST request with JSON');
32 }
33});
34
35server.listen(3000);Serving Static Files#
1import { createServer } from 'node:http';
2import { readFile } from 'node:fs/promises';
3import { join, extname } from 'node:path';
4
5const MIME_TYPES = {
6 '.html': 'text/html',
7 '.css': 'text/css',
8 '.js': 'text/javascript',
9 '.json': 'application/json',
10 '.png': 'image/png',
11 '.jpg': 'image/jpeg',
12 '.svg': 'image/svg+xml',
13};
14
15const server = createServer(async (req, res) => {
16 const url = new URL(req.url, `http://${req.headers.host}`);
17 let filepath = join(process.cwd(), 'public', url.pathname);
18
19 // Default to index.html
20 if (url.pathname === '/') {
21 filepath = join(process.cwd(), 'public', 'index.html');
22 }
23
24 try {
25 const content = await readFile(filepath);
26 const ext = extname(filepath);
27 const contentType = MIME_TYPES[ext] || 'application/octet-stream';
28
29 res.writeHead(200, { 'Content-Type': contentType });
30 res.end(content);
31 } catch (error) {
32 if (error.code === 'ENOENT') {
33 res.writeHead(404);
34 res.end('Not Found');
35 } else {
36 res.writeHead(500);
37 res.end('Server Error');
38 }
39 }
40});
41
42server.listen(3000);HTTP Client (Making Requests)#
1import { request, get } from 'node:http';
2import https from 'node:https';
3
4// Simple GET request
5function httpGet(url) {
6 return new Promise((resolve, reject) => {
7 const protocol = url.startsWith('https') ? https : http;
8
9 protocol.get(url, (res) => {
10 let data = '';
11 res.on('data', chunk => data += chunk);
12 res.on('end', () => resolve({
13 statusCode: res.statusCode,
14 headers: res.headers,
15 body: data
16 }));
17 }).on('error', reject);
18 });
19}
20
21// POST request
22function httpPost(url, body) {
23 return new Promise((resolve, reject) => {
24 const urlObj = new URL(url);
25 const data = JSON.stringify(body);
26
27 const options = {
28 hostname: urlObj.hostname,
29 port: urlObj.port,
30 path: urlObj.pathname,
31 method: 'POST',
32 headers: {
33 'Content-Type': 'application/json',
34 'Content-Length': Buffer.byteLength(data)
35 }
36 };
37
38 const req = request(options, (res) => {
39 let responseData = '';
40 res.on('data', chunk => responseData += chunk);
41 res.on('end', () => resolve({
42 statusCode: res.statusCode,
43 body: responseData
44 }));
45 });
46
47 req.on('error', reject);
48 req.write(data);
49 req.end();
50 });
51}
52
53// Usage
54const response = await httpGet('http://api.example.com/data');
55const postResponse = await httpPost('http://api.example.com/users', {
56 name: 'John'
57});Server Events#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 res.end('OK');
5});
6
7// Server events
8server.on('listening', () => {
9 console.log('Server is listening');
10});
11
12server.on('connection', (socket) => {
13 console.log('New connection from', socket.remoteAddress);
14});
15
16server.on('request', (req, res) => {
17 console.log(`${req.method} ${req.url}`);
18});
19
20server.on('close', () => {
21 console.log('Server closed');
22});
23
24server.on('error', (error) => {
25 if (error.code === 'EADDRINUSE') {
26 console.log('Port is already in use');
27 } else {
28 console.error('Server error:', error);
29 }
30});
31
32server.listen(3000);Keep-Alive and Timeouts#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 res.end('OK');
5});
6
7// Connection timeout (default: 0, no timeout)
8server.timeout = 30000; // 30 seconds
9
10// Keep-alive timeout
11server.keepAliveTimeout = 5000; // 5 seconds
12
13// Headers timeout
14server.headersTimeout = 60000; // 60 seconds
15
16// Request timeout
17server.requestTimeout = 300000; // 5 minutes
18
19// Per-request timeout
20server.on('request', (req, res) => {
21 req.setTimeout(10000, () => {
22 res.writeHead(408);
23 res.end('Request Timeout');
24 });
25});
26
27server.listen(3000);HTTPS Server#
1import { createServer } from 'node:https';
2import { readFileSync } from 'node:fs';
3
4const options = {
5 key: readFileSync('private-key.pem'),
6 cert: readFileSync('certificate.pem'),
7};
8
9const server = createServer(options, (req, res) => {
10 res.writeHead(200);
11 res.end('Secure Hello!');
12});
13
14server.listen(443, () => {
15 console.log('HTTPS server running on port 443');
16});
17
18// Redirect HTTP to HTTPS
19import { createServer as createHttpServer } from 'node:http';
20
21createHttpServer((req, res) => {
22 res.writeHead(301, {
23 Location: `https://${req.headers.host}${req.url}`
24 });
25 res.end();
26}).listen(80);Streaming Response#
1import { createServer } from 'node:http';
2import { createReadStream } from 'node:fs';
3
4const server = createServer((req, res) => {
5 if (req.url === '/video') {
6 res.writeHead(200, { 'Content-Type': 'video/mp4' });
7 createReadStream('./video.mp4').pipe(res);
8 }
9 else if (req.url === '/stream') {
10 res.writeHead(200, {
11 'Content-Type': 'text/event-stream',
12 'Cache-Control': 'no-cache',
13 'Connection': 'keep-alive'
14 });
15
16 // Server-Sent Events
17 let count = 0;
18 const interval = setInterval(() => {
19 res.write(`data: ${JSON.stringify({ count: ++count })}\n\n`);
20
21 if (count >= 10) {
22 clearInterval(interval);
23 res.end();
24 }
25 }, 1000);
26
27 req.on('close', () => clearInterval(interval));
28 }
29});
30
31server.listen(3000);Graceful Shutdown#
1import { createServer } from 'node:http';
2
3const server = createServer((req, res) => {
4 res.end('OK');
5});
6
7const connections = new Set();
8
9server.on('connection', (conn) => {
10 connections.add(conn);
11 conn.on('close', () => connections.delete(conn));
12});
13
14function shutdown() {
15 console.log('Shutting down...');
16
17 // Stop accepting new connections
18 server.close(() => {
19 console.log('Server closed');
20 process.exit(0);
21 });
22
23 // Close existing connections
24 for (const conn of connections) {
25 conn.end();
26 }
27
28 // Force close after timeout
29 setTimeout(() => {
30 for (const conn of connections) {
31 conn.destroy();
32 }
33 process.exit(1);
34 }, 10000);
35}
36
37process.on('SIGTERM', shutdown);
38process.on('SIGINT', shutdown);
39
40server.listen(3000);Best Practices#
Server:
✓ Handle errors properly
✓ Set appropriate timeouts
✓ Implement graceful shutdown
✓ Use streams for large data
Security:
✓ Validate input
✓ Set security headers
✓ Use HTTPS in production
✓ Limit request size
Performance:
✓ Use keep-alive
✓ Enable compression
✓ Cache responses
✓ Stream large files
Avoid:
✗ Blocking the event loop
✗ Ignoring errors
✗ Memory leaks on connections
✗ Trusting client headers
Conclusion#
The HTTP module provides low-level control for building web servers in Node.js. While frameworks like Express abstract much of this, understanding the core module is valuable. Use it for custom servers, learning purposes, or when you need minimal dependencies. For production, consider using established frameworks that handle security, routing, and middleware out of the box.