HTTP & Web Servers

15 questions found

How would you implement HTTP response streaming in Node.js to begin sending data to a client before the entire response is ready, such as for a large report?

Advanced
Rather than building a complete response in memory before calling res.end() once, you can call res.write() multiple times as data becomes progressively available, letting the client start receiving and processing data before the entire operation on the server side has finished -- particularly valuable for large exports or reports where waiting for the entire dataset to be assembled before sending anything would introduce significant unnecessary latency for the client.
app.get('/export', async (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/csv', 'Transfer-Encoding': 'chunked' });
  res.write('id,name,email\n');
  for await (const batch of getRecordsInBatches()) {
    res.write(batch.map(r => `${r.id},${r.name},${r.email}\n`).join(''));
  }
  res.end();
});
Real-world example A CSV export endpoint streams rows to the client as they're fetched from the database in batches, letting a download begin immediately and progressively fill in, rather than the user waiting with no feedback until the entire multi-hundred-thousand-row export had been fully assembled server-side first.

Common follow-ups: How does this relate to and differ from using a proper Node.js Transform/Readable stream piped directly to the response object?;What HTTP header indicates to the client that a response is being sent in chunks rather than with a known total Content-Length?

Streams & Buffers;Performance Optimization & Profiling

What is the difference between a 301 and a 302 HTTP redirect, and when should a Node.js application use each?

Intermediate
A 301 (Moved Permanently) redirect tells the client (and search engines) that a resource has permanently moved to a new URL, and future requests should go directly to the new location, with browsers and search engines typically caching this redirect and updating their own references. A 302 (Found, or historically 'Moved Temporarily') indicates the move is temporary, and the client should continue using the original URL for future requests -- using the wrong status code can cause search engines to incorrectly deindex a URL permanently, or fail to update their index when a change genuinely is permanent.
app.get('/old-path', (req, res) => res.redirect(301, '/new-path')); // permanent, update bookmarks/SEO
app.get('/maintenance-redirect', (req, res) => res.redirect(302, '/status')); // temporary
Real-world example A company permanently renaming a product's URL structure uses a 301 redirect from the old URLs to the new ones, ensuring search engines transfer the old page's accumulated SEO ranking to the new URL rather than treating it as an entirely new, unranked page.

Common follow-ups: What SEO consequence results from mistakenly using a 302 for a change that's actually permanent?;What's the difference between a 302 and a 307 redirect regarding whether the request method and body are preserved?

RESTful API Design with Express;Search Engine Optimization

How would you implement a simple TCP-level proxy or load balancer in Node.js using the net module, distinct from an HTTP-level reverse proxy?

Advanced
The node:net module operates at the raw TCP level, below HTTP, letting you build a proxy that forwards raw byte streams between a client and a backend server without parsing or understanding HTTP semantics at all -- useful for protocols other than HTTP, or for a lightweight load-balancing layer that simply distributes TCP connections across backend servers without needing to inspect or modify the application-level protocol being carried over those connections.
const net = require('node:net');

const servers = [{ host: 'backend1', port: 4000 }, { host: 'backend2', port: 4000 }];
let current = 0;

net.createServer((clientSocket) => {
  const backend = servers[current++ % servers.length]; // simple round-robin
  const backendSocket = net.connect(backend, () => {
    clientSocket.pipe(backendSocket);
    backendSocket.pipe(clientSocket);
  });
}).listen(3000);
Real-world example A team building a lightweight load balancer for a non-HTTP TCP-based protocol (like a custom binary protocol between internal services) implements it using the raw net module, since an HTTP-aware reverse proxy like Nginx wouldn't understand or correctly route the underlying protocol being used.

Common follow-ups: Why would you choose this raw TCP approach over an HTTP-level reverse proxy for an application that does happen to use HTTP?;What features (like HTTP header inspection or path-based routing) does this simple TCP proxy approach necessarily give up compared to an HTTP-aware proxy?

Advanced Node.js;Cloud & DevOps

What is the purpose of the Content-Length and Transfer-Encoding: chunked headers, and why can't a response use both simultaneously?

Intermediate
Content-Length tells the client exactly how many bytes to expect in the response body, requiring the server to know the complete size upfront before sending anything. Transfer-Encoding: chunked instead sends the response in a series of independently-sized chunks without needing to know the total size in advance, letting the server begin sending data before it's finished generating the complete response -- the two are mutually exclusive because Content-Length promises an exact total size, which is fundamentally incompatible with a chunked response whose total length isn't known until the last chunk is sent.
// Content-Length: server knows the exact size upfront
const body = JSON.stringify(data);
res.writeHead(200, { 'Content-Length': Buffer.byteLength(body) });
res.end(body);

// Transfer-Encoding: chunked (Express sets this automatically when streaming
// without an explicit Content-Length, via multiple res.write() calls)
res.write(chunk1);
res.write(chunk2);
res.end();
Real-world example A streaming report-generation endpoint necessarily uses chunked transfer encoding (by simply calling res.write() multiple times without setting Content-Length) since the total size of the generated report genuinely isn't known until the entire generation process has finished.

Common follow-ups: What happens if a server mistakenly sends both a Content-Length header and uses chunked encoding at the same time?;How does HTTP/2 handle the concept of framing data differently, given it doesn't actually use chunked transfer encoding the way HTTP/1.1 does?

Streams & Buffers;Performance Optimization & Profiling

What is the difference between an HTTP request header and a query parameter, and when should each be used to pass information to a Node.js server?

Beginner
Headers carry metadata about the request itself (authentication tokens, content type, accepted formats) that's generally not part of the actual resource being requested, while query parameters are part of the URL and typically represent filtering, sorting, or pagination options for the specific resource being requested -- a general convention is that headers carry cross-cutting concerns applicable across many different endpoints, while query parameters carry endpoint-specific data relevant to that particular request.
// Header: cross-cutting concern (auth), same pattern across many endpoints
fetch('/api/orders', { headers: { Authorization: 'Bearer token123' } });

// Query parameters: specific to this endpoint's filtering/pagination needs
fetch('/api/orders?status=shipped&page=2&limit=20');
Real-world example An API consistently uses an Authorization header for authentication across every single endpoint (since it's a cross-cutting concern), while using endpoint-specific query parameters like ?status=shipped for filtering results, following a clear, predictable convention rather than mixing the two purposes inconsistently.

Common follow-ups: Why is it generally considered bad practice to put sensitive data like an API key in a query parameter rather than a header?;How would you decide whether a piece of data belongs in a route parameter (like /orders/:id) versus a query parameter?

Security;RESTful API Design with Express

Showing 11–15 of 15