HyperExpress v7 modernizes the native foundation and fixes request, response, middleware, multipart, file, and WebSocket lifecycle races. It remains CommonJS-only, keeps snake_case names, and preserves the existing route overloads and opinionated HyperExpress API.
- Node.js 22, 24, and 26 are supported. Older releases and odd-numbered Node.js releases are outside the v7 support range.
- uWebSockets.js is pinned to v20.69.0.
- Use a glibc-based Linux image such as Debian or Ubuntu. Plain musl Alpine and Alpine with
gcompatare unsupported because the native addon can terminate withSIGSEGV.
Synchronous middleware must continue calling next(). Middleware that returns any thenable advances when fulfilled; throwing, rejecting, calling next(error), or fulfilling with an Error invokes error handling.
Each middleware completes at most once. The first next() returns true; later calls return false and cannot advance the chain. Promise settlements after next() are also ignored. Enable the server option strict_middleware: true to report duplicate completion to the applicable scoped error handler while still preventing double advancement.
Routers now provide set_error_handler() and set_not_found_handler(). Route errors walk from the innermost mounted router through its parents to the server. Unmatched requests select the longest matching mount boundary; equal boundaries use first-mount order before parent/server fallback. Handlers assigned before or after mounting work, including routers mounted more than once.
Response.header() continues to append by default. Pass true as its overwrite argument to replace existing values. Header lookup is case-insensitive and compatibility helpers are chainable.
html(), json(), and jsonp() now emit UTF-8 charsets. send() remains content-type-neutral. HyperExpress does not synthesize ETags or Express policy headers; uWebSockets.js ordinarily owns content length, date, and connection handling. SSE preserves the v6 Connection: keep-alive and X-Accel-Buffering: no headers. JSONP callback names and attachment filenames are sanitized, and jsonp() falls back to JSON when no callback is available.
Response.begin_write() exposes the native beginWrite() operation. In pinned uWebSockets.js v20.69.0, following it with the ordinary body-write path inserts an extra CRLF that some HTTP clients reject. Treat it as a direct upstream primitive and do not combine it with write(), stream(), or send() until upstream resolves that behavior.
Ordinary readable.pipe(response) remains supported. It now explicitly bridges Node and uWebSockets.js backpressure, routes source errors through the scoped error handler, and destroys an active source when the response aborts.
Fixed-length, empty, and transfer-encoded request bodies now use one eagerly bound native receiver. Concurrent calls to each body helper share its in-flight promise; after success, later calls resolve from the parsed value cache, including falsey values. Different helpers can continue reading the same retained body. Limits settle every parser exactly once, and completed requests no longer attempt a native resume.
Multipart handlers are serialized and may return any thenable. HyperExpress waits for all field/file handlers, drains unconsumed files, and propagates source, Busboy, and handler errors once. MultipartField.write() now handles source and destination failures through pipeline semantics.
The live-file cache belongs to each server. Absolute cache keys are normalized, watchers are disposed on eviction/error/server close, and LiveFile.close() is idempotent.
WebSocket send() and ping() retain the native numeric status values: 1 means sent, 0 means backpressure, and 2 means dropped. Code comparing these values with booleans must be updated. Fragment streaming waits for the final fragment, handles empty streams, and rejects source/socket/drop failures.
New WebSocket route options are close_on_backpressure_limit, max_lifetime, and send_pings_automatically. All three are opt-in: omitting them preserves the native behavior and does not impose a connection lifetime. New events are dropped, subscription, and error. Synchronous exceptions and rejected thenables from listeners reach error; if unhandled, the socket closes with code 1011.
message_type: 'ArrayBuffer' retains its v6 zero-copy, callback-lifetime behavior. Use the new ArrayBufferSafe mode to receive a copied ArrayBuffer that can safely be retained by asynchronous work. Buffer messages also remain safe to retain.
New connection metadata includes Request.port, Request.proxy_port, and Websocket.remote_port. Worker composition is available through Server.get_descriptor(), add_child_app_descriptor(), and remove_child_app_descriptor().
Request connection metadata is now captured at HTTP request entry. This is required because pinned uWebSockets.js invalidates HttpResponse after completion and replaces its per-socket HTTP data during WebSocket upgrade; resolving an address lazily after either transition can return an empty value or access a discarded native wrapper. The stable snapshot remains available for the full HyperExpress request/WebSocket lifetime.
Native WebSocket configuration is validated before registration. In particular, idle_timeout is 0 or 8..960 seconds, max_lifetime is 0..239 minutes, signed-addon byte limits cannot overflow, and compression must be a valid uWebSockets.js preset combination. Ping and close payload limits are enforced rather than relying on native truncation.
shutdown() stops accepting first and drains pending HTTP requests; long-lived WebSockets are not counted. force_close() explicitly closes all native sockets. Listen/shutdown/close calls are idempotent, process listeners are removed, and a server can safely listen again after graceful shutdown.
Listen handles are ownership-tracked. HyperExpress will not pass a foreign or already-closed token into us_listen_socket_close, and failures inside a listen callback close the token exactly once.
Existing routing overloads remain intact. Declarations now reflect ArrayBuffer views, route streaming, multipart promises, scoped router handlers, ports, descriptors, response lifecycle methods, and numeric WebSocket send statuses. These corrections may reveal code that previously depended on inaccurate declarations.