Back to Blog
Node.jsHTTPServerNetworking

Node.js HTTP Module Deep Dive

Master the Node.js HTTP module. From basic servers to streaming to HTTPS configuration.

B
Bootspring Team
Engineering
January 4, 2021
8 min read

The HTTP module is foundational to Node.js web servers. Here's a comprehensive guide.

Basic HTTP Server#

1const http = require('http'); 2 3const server = http.createServer((req, res) => { 4 // Request info 5 console.log('Method:', req.method); 6 console.log('URL:', req.url); 7 console.log('Headers:', req.headers); 8 9 // Send response 10 res.statusCode = 200; 11 res.setHeader('Content-Type', 'text/plain'); 12 res.end('Hello, World!\n'); 13}); 14 15server.listen(3000, '127.0.0.1', () => { 16 console.log('Server running at http://127.0.0.1:3000/'); 17}); 18 19// Handle server errors 20server.on('error', (err) => { 21 if (err.code === 'EADDRINUSE') { 22 console.error('Port already in use'); 23 } else { 24 console.error('Server error:', err); 25 } 26});

Request Handling#

1const http = require('http'); 2const url = require('url'); 3 4const server = http.createServer((req, res) => { 5 // Parse URL 6 const parsedUrl = url.parse(req.url, true); 7 const pathname = parsedUrl.pathname; 8 const query = parsedUrl.query; 9 10 // Modern URL API 11 const fullUrl = new URL(req.url, `http://${req.headers.host}`); 12 const searchParams = fullUrl.searchParams; 13 14 // Read request body 15 let body = ''; 16 req.on('data', (chunk) => { 17 body += chunk.toString(); 18 }); 19 20 req.on('end', () => { 21 // Parse JSON body 22 let data = null; 23 if (req.headers['content-type'] === 'application/json') { 24 try { 25 data = JSON.parse(body); 26 } catch (e) { 27 res.statusCode = 400; 28 res.end('Invalid JSON'); 29 return; 30 } 31 } 32 33 // Handle request 34 handleRequest(req, res, { pathname, query, data }); 35 }); 36}); 37 38function handleRequest(req, res, { pathname, query, data }) { 39 if (req.method === 'GET' && pathname === '/api/users') { 40 res.setHeader('Content-Type', 'application/json'); 41 res.end(JSON.stringify({ users: [] })); 42 } else if (req.method === 'POST' && pathname === '/api/users') { 43 res.setHeader('Content-Type', 'application/json'); 44 res.statusCode = 201; 45 res.end(JSON.stringify({ created: data })); 46 } else { 47 res.statusCode = 404; 48 res.end('Not Found'); 49 } 50}

Response Methods#

1const http = require('http'); 2 3const server = http.createServer((req, res) => { 4 // Set status code 5 res.statusCode = 200; 6 // Or 7 res.writeHead(200, { 8 'Content-Type': 'application/json', 9 'Cache-Control': 'no-cache', 10 'X-Custom-Header': 'value', 11 }); 12 13 // Set individual headers 14 res.setHeader('Content-Type', 'text/html'); 15 res.setHeader('Set-Cookie', ['session=abc123', 'user=john']); 16 17 // Check if header was sent 18 if (!res.headersSent) { 19 res.setHeader('X-Late-Header', 'value'); 20 } 21 22 // Write body in chunks 23 res.write('First chunk\n'); 24 res.write('Second chunk\n'); 25 26 // End response 27 res.end('Final chunk\n'); 28 29 // Or end with callback 30 res.end('Done', 'utf8', () => { 31 console.log('Response sent'); 32 }); 33});

Routing#

1const http = require('http'); 2 3// Simple router 4class Router { 5 constructor() { 6 this.routes = { 7 GET: {}, 8 POST: {}, 9 PUT: {}, 10 DELETE: {}, 11 }; 12 } 13 14 get(path, handler) { 15 this.routes.GET[path] = handler; 16 } 17 18 post(path, handler) { 19 this.routes.POST[path] = handler; 20 } 21 22 put(path, handler) { 23 this.routes.PUT[path] = handler; 24 } 25 26 delete(path, handler) { 27 this.routes.DELETE[path] = handler; 28 } 29 30 handle(req, res) { 31 const method = req.method; 32 const url = new URL(req.url, `http://${req.headers.host}`); 33 const pathname = url.pathname; 34 35 const handler = this.routes[method]?.[pathname]; 36 37 if (handler) { 38 handler(req, res, url); 39 } else { 40 res.statusCode = 404; 41 res.end('Not Found'); 42 } 43 } 44} 45 46// Usage 47const router = new Router(); 48 49router.get('/', (req, res) => { 50 res.end('Home'); 51}); 52 53router.get('/api/users', (req, res) => { 54 res.setHeader('Content-Type', 'application/json'); 55 res.end(JSON.stringify({ users: [] })); 56}); 57 58router.post('/api/users', async (req, res) => { 59 const body = await parseBody(req); 60 res.statusCode = 201; 61 res.end(JSON.stringify({ created: body })); 62}); 63 64const server = http.createServer((req, res) => { 65 router.handle(req, res); 66}); 67 68// Parse body helper 69function parseBody(req) { 70 return new Promise((resolve, reject) => { 71 let body = ''; 72 req.on('data', chunk => body += chunk); 73 req.on('end', () => { 74 try { 75 resolve(JSON.parse(body)); 76 } catch { 77 resolve(body); 78 } 79 }); 80 req.on('error', reject); 81 }); 82}

Streaming#

1const http = require('http'); 2const fs = require('fs'); 3const path = require('path'); 4 5const server = http.createServer((req, res) => { 6 if (req.url === '/video') { 7 const videoPath = path.join(__dirname, 'video.mp4'); 8 const stat = fs.statSync(videoPath); 9 const fileSize = stat.size; 10 const range = req.headers.range; 11 12 if (range) { 13 // Range request for video streaming 14 const parts = range.replace(/bytes=/, '').split('-'); 15 const start = parseInt(parts[0], 10); 16 const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; 17 const chunkSize = end - start + 1; 18 19 res.writeHead(206, { 20 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 21 'Accept-Ranges': 'bytes', 22 'Content-Length': chunkSize, 23 'Content-Type': 'video/mp4', 24 }); 25 26 const stream = fs.createReadStream(videoPath, { start, end }); 27 stream.pipe(res); 28 } else { 29 // Full file 30 res.writeHead(200, { 31 'Content-Length': fileSize, 32 'Content-Type': 'video/mp4', 33 }); 34 fs.createReadStream(videoPath).pipe(res); 35 } 36 } 37 38 // Stream large JSON 39 if (req.url === '/large-data') { 40 res.setHeader('Content-Type', 'application/json'); 41 res.write('['); 42 43 let first = true; 44 for (let i = 0; i < 10000; i++) { 45 if (!first) res.write(','); 46 first = false; 47 res.write(JSON.stringify({ id: i, data: `Item ${i}` })); 48 } 49 50 res.write(']'); 51 res.end(); 52 } 53});

HTTPS Server#

1const https = require('https'); 2const fs = require('fs'); 3 4// Read SSL certificates 5const options = { 6 key: fs.readFileSync('private-key.pem'), 7 cert: fs.readFileSync('certificate.pem'), 8 // Optional: CA chain 9 ca: fs.readFileSync('ca-chain.pem'), 10}; 11 12const server = https.createServer(options, (req, res) => { 13 res.writeHead(200); 14 res.end('Secure Hello World\n'); 15}); 16 17server.listen(443, () => { 18 console.log('HTTPS server running on port 443'); 19}); 20 21// Redirect HTTP to HTTPS 22const http = require('http'); 23 24http.createServer((req, res) => { 25 res.writeHead(301, { 26 Location: `https://${req.headers.host}${req.url}`, 27 }); 28 res.end(); 29}).listen(80);

HTTP Client#

1const http = require('http'); 2const https = require('https'); 3 4// GET request 5function get(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', () => { 13 resolve({ 14 statusCode: res.statusCode, 15 headers: res.headers, 16 body: data, 17 }); 18 }); 19 }).on('error', reject); 20 }); 21} 22 23// POST request 24function post(url, data) { 25 return new Promise((resolve, reject) => { 26 const urlObj = new URL(url); 27 const protocol = urlObj.protocol === 'https:' ? https : http; 28 const body = JSON.stringify(data); 29 30 const options = { 31 hostname: urlObj.hostname, 32 port: urlObj.port, 33 path: urlObj.pathname + urlObj.search, 34 method: 'POST', 35 headers: { 36 'Content-Type': 'application/json', 37 'Content-Length': Buffer.byteLength(body), 38 }, 39 }; 40 41 const req = protocol.request(options, (res) => { 42 let responseData = ''; 43 res.on('data', chunk => responseData += chunk); 44 res.on('end', () => { 45 resolve({ 46 statusCode: res.statusCode, 47 headers: res.headers, 48 body: responseData, 49 }); 50 }); 51 }); 52 53 req.on('error', reject); 54 req.write(body); 55 req.end(); 56 }); 57} 58 59// Usage 60async function main() { 61 const getResult = await get('https://api.example.com/users'); 62 console.log(getResult); 63 64 const postResult = await post('https://api.example.com/users', { 65 name: 'John', 66 }); 67 console.log(postResult); 68}

Keep-Alive and Connection Pooling#

1const http = require('http'); 2 3// Server with keep-alive 4const server = http.createServer((req, res) => { 5 res.setHeader('Connection', 'keep-alive'); 6 res.setHeader('Keep-Alive', 'timeout=5, max=1000'); 7 res.end('Hello'); 8}); 9 10server.keepAliveTimeout = 5000; 11server.headersTimeout = 6000; 12 13// Client with agent for connection pooling 14const agent = new http.Agent({ 15 keepAlive: true, 16 keepAliveMsecs: 1000, 17 maxSockets: 10, 18 maxFreeSockets: 5, 19}); 20 21function makeRequest() { 22 return new Promise((resolve, reject) => { 23 const req = http.request({ 24 hostname: 'localhost', 25 port: 3000, 26 path: '/', 27 agent: agent, 28 }, (res) => { 29 let data = ''; 30 res.on('data', chunk => data += chunk); 31 res.on('end', () => resolve(data)); 32 }); 33 34 req.on('error', reject); 35 req.end(); 36 }); 37} 38 39// Reuse connections 40async function benchmark() { 41 for (let i = 0; i < 100; i++) { 42 await makeRequest(); 43 } 44 console.log('Agent stats:', agent.getCurrentStatus()); 45}

Middleware Pattern#

1const http = require('http'); 2 3// Middleware handler 4function createHandler(middlewares) { 5 return (req, res) => { 6 let index = 0; 7 8 function next(err) { 9 if (err) { 10 res.statusCode = 500; 11 res.end('Internal Server Error'); 12 return; 13 } 14 15 const middleware = middlewares[index++]; 16 if (middleware) { 17 try { 18 middleware(req, res, next); 19 } catch (e) { 20 next(e); 21 } 22 } 23 } 24 25 next(); 26 }; 27} 28 29// Example middlewares 30function logger(req, res, next) { 31 console.log(`${req.method} ${req.url}`); 32 next(); 33} 34 35function cors(req, res, next) { 36 res.setHeader('Access-Control-Allow-Origin', '*'); 37 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); 38 next(); 39} 40 41function jsonParser(req, res, next) { 42 if (req.headers['content-type'] === 'application/json') { 43 let body = ''; 44 req.on('data', chunk => body += chunk); 45 req.on('end', () => { 46 try { 47 req.body = JSON.parse(body); 48 next(); 49 } catch { 50 res.statusCode = 400; 51 res.end('Invalid JSON'); 52 } 53 }); 54 } else { 55 next(); 56 } 57} 58 59function handler(req, res) { 60 res.setHeader('Content-Type', 'application/json'); 61 res.end(JSON.stringify({ message: 'Hello', body: req.body })); 62} 63 64// Create server with middleware 65const server = http.createServer( 66 createHandler([logger, cors, jsonParser, handler]) 67);

Server-Sent Events#

1const http = require('http'); 2 3const server = http.createServer((req, res) => { 4 if (req.url === '/events') { 5 res.writeHead(200, { 6 'Content-Type': 'text/event-stream', 7 'Cache-Control': 'no-cache', 8 'Connection': 'keep-alive', 9 }); 10 11 // Send event every second 12 const interval = setInterval(() => { 13 const data = JSON.stringify({ 14 time: new Date().toISOString(), 15 value: Math.random(), 16 }); 17 res.write(`data: ${data}\n\n`); 18 }, 1000); 19 20 // Named events 21 setInterval(() => { 22 res.write(`event: ping\n`); 23 res.write(`data: ${Date.now()}\n\n`); 24 }, 5000); 25 26 // Clean up on close 27 req.on('close', () => { 28 clearInterval(interval); 29 }); 30 } 31}); 32 33server.listen(3000);

Best Practices#

Server Setup: ✓ Handle errors properly ✓ Set appropriate timeouts ✓ Use keep-alive for performance ✓ Configure headers securely Request Handling: ✓ Validate input data ✓ Parse body asynchronously ✓ Handle errors in body parsing ✓ Use streaming for large data Response: ✓ Set correct Content-Type ✓ Use appropriate status codes ✓ Stream large responses ✓ Handle back-pressure Security: ✓ Use HTTPS in production ✓ Set security headers ✓ Validate request size ✓ Rate limit requests

Conclusion#

The Node.js HTTP module provides low-level control over HTTP servers and clients. Use it for learning HTTP fundamentals or building custom solutions. For production apps, consider frameworks like Express or Fastify that build on these primitives.

Share this article

Help spread the word about Bootspring