Back to Blog
Node.jsHTTPServerBackend

Node.js HTTP Module Guide

Master the Node.js HTTP module for building web servers and making requests.

B
Bootspring Team
Engineering
October 23, 2018
7 min read

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.

Share this article

Help spread the word about Bootspring