
ServerResponse forms the backbone of outbound data transmission in Node.js HTTP servers through the built-in http module. Developers access it as the second parameter in request listeners, enabling precise control over status codes, headers, and body content sent to clients. The class extends the Writable stream, supporting both buffered and chunked outputs for varied response sizes.
Core properties include statusCode, which defaults to 200 and accepts integer values from 100 to 599 for signaling request outcomes. statusMessage provides a short textual description that Node.js populates automatically unless overridden. headersSent indicates whether the response header block has already been transmitted, preventing duplicate writes. The socket property exposes the underlying net.Socket for low-level TCP operations such as setting keep-alive timeouts.
Methods begin with writeHead(statusCode, statusMessage, headers), which sends the status line and initial headers in one call. Developers often combine it with setHeader for incremental header assignment before flushing. write(chunk, encoding, callback) appends data to the response body while returning a boolean that signals backpressure. end(chunk, encoding, callback) finalizes the response, optionally appending a final chunk and triggering the finish event. addTrailers(headers) appends HTTP trailers after the body completes, useful for checksums or metrics when chunked transfer encoding is active.
Header management follows strict sequencing rules. setHeader(name, value) queues values without immediate transmission, while getHeader(name) retrieves previously set values. removeHeader(name) deletes entries before headers are sent. The getHeaders() method returns a shallow copy of the current header object for inspection. Case-insensitive matching applies to header names per HTTP specification, yet Node.js preserves the original casing supplied by the developer.
Status code handling integrates directly with writeHead or the statusCode property. Common patterns include 301 for permanent redirects via location header, 404 for missing resources, and 500 for internal errors. Developers frequently wrap these in helper functions that standardize JSON error payloads containing code, message, and timestamp fields.
Streaming responses leverage pipe() from readable sources such as file streams or database cursors. This approach minimizes memory usage for large assets by transferring data in 64 KB chunks by default. Compression modules like zlib integrate via response streams, applying gzip or deflate encoding when the accept-encoding header requests it. Developers must set content-encoding and vary headers correctly to avoid cache mismatches.
Error scenarios arise when attempting writes after end() or when sockets close prematurely. The response object emits error events that require explicit listeners to prevent process crashes. Timeout configuration via socket.setTimeout() allows graceful termination of slow clients, typically set between 30 and 120 seconds depending on endpoint complexity.
Performance considerations include disabling the Nagle algorithm with socket.setNoDelay(true) for low-latency APIs. Reusing keep-alive connections reduces TCP handshake overhead when clients issue multiple requests. Buffer pooling through Buffer.allocUnsafe() accelerates body construction for high-throughput endpoints, although developers must clear sensitive data afterward.
Framework integration appears in Express.js where res.status(), res.json(), and res.send() wrap the native ServerResponse. These abstractions automatically set content-type and serialize objects while preserving access to the original res object for custom header manipulation. Similar patterns exist in Fastify and Koa, each extending the base class with context-aware helpers.
Security practices mandate validation of all outgoing headers to prevent injection attacks. Content-Security-Policy and X-Content-Type-Options headers mitigate XSS and MIME-sniffing risks. Rate-limiting logic often tracks response counts per IP using the socket.remoteAddress property before invoking end().
Testing ServerResponse behavior involves supertest or node-mocks-http libraries that simulate request objects and capture emitted headers and body chunks. Assertions verify status codes, header presence, and payload integrity without spinning up full HTTP listeners.
Advanced customization extends the prototype with domain-specific methods, such as a sendFile helper that handles MIME detection via mime-types package and streams file contents with appropriate cache-control directives. Conditional responses using if-modified-since headers leverage fs.stat to return 304 when files remain unchanged.
Observability improves through integration with diagnostic channels that emit events on header writes and body completion. These hooks feed into APM tools for latency histograms and error rate tracking across production deployments.
Cross-version differences appear between Node.js 14 and 18, where the latter introduces stricter header validation and improved HTTP/2 support via http2.Http2ServerResponse subclass. Developers targeting HTTP/2 must handle push streams separately while retaining identical status and header APIs.
Resource cleanup requires closing any open file descriptors or database cursors before calling end(), ensuring no memory leaks accumulate under sustained load. Event listeners attached to the response should be removed after finish to avoid retaining references.
The combination of these properties, methods, and patterns enables developers to construct reliable, efficient, and secure HTTP responses tailored to application requirements.