Open source
HyperExpress I created and maintain HyperExpress, a Node.js HTTP/WebSocket framework.
HyperExpress combines an Express-like JavaScript API with uWebSockets.js for HTTP and WebSocket services. It includes routing, middleware, server-sent events, multipart uploads, and TLS host support. Compatibility with Express middleware is partial.
2K+ GitHub stars · 5K+ npm downloads in the week of September 10–16, 2026
GitHub snapshot: 18 September 2026. Downloads count package fetches, not unique users.
Public library / conceptual flow Request lifecycle
Application Handlers & streams HyperExpress Routing & middleware uWebSockets.js Native binding Transport HTTP / WebSockets Explicit ownership. Completion guards. Backpressure-aware output. Based on the project README and native lifecycle contract.
Working across the native boundary Working across the native boundary means I have to account for the lifetime of objects and handles. Native request objects are only valid inside their callback, so HyperExpress copies request metadata at entry and guards response operations after completion. Incoming callback memory must also be copied before it is retained. The shutdown contract tracks listen-token ownership and forbids closing foreign or already-closed handles, keeping cleanup subject to the same ownership rules.
Engineering lens / HyperExpress Explore payload ownership
What survives the callback? Follow the lifetime of a WebSocket message.
ArrayBuffer ArrayBufferSafe
Callback begins Callback returns Async work
Callback lifetime
Callback-owned memory
Retain the message? Copy it before the callback ends.
WebSocket payload ownership is explicit: callback-lifetime ArrayBuffer messages are distinct from copied ArrayBufferSafe messages that can survive asynchronous work. This exposes a choice between retention safety and copying.
Completing requests once Middleware can call next() and later settle its returned promise, or accidentally call next() twice. In HyperExpress, I account for that by advancing the chain only once. The regression scenario checks that the first completion succeeds and the duplicate is ignored. This matters at the framework boundary because an application mistake should not accidentally dispatch the next handler a second time.
Streaming to slow consumers In the streaming implementation I maintain, a partially accepted write is not treated as a completed chunk. The code tracks the native write offset and retries the remaining bytes after drain. It also checks a declared content length against the bytes read and destroys the source stream when the response closes. Those details tie transport progress, source cleanup, and response completion into one lifecycle.
Keeping the project maintainable I maintain separate runtime, TypeScript, and load-test commands for the project. The load gate covers HTTP, multipart handling, aborts, WebSockets, and memory stress, rather than just a successful request. Version 7 supports Node.js 22, 24, and 26 while preserving CommonJS and snake_case APIs. It also pins uWebSockets.js to v20.69.0, making the native dependency version an explicit part of the supported setup.