Skip to content

Patterns from Node.js Ecosystem

Node.js, Redux, and XState demonstrate event-driven and state management patterns at scale.

PatternProjectWhereWhat It Does
Observer / Pub-SubNode.jslib/events.jsEventEmitter — foundation of Node's event-driven architecture
Observer / Pub-SubReduxcreateStore.tssubscribe() + dispatch() — state change notification
State MachineXStateStateMachine.tsIndustry-standard finite state machine library
BackpressureNode.jswritable.jswriteOrBuffer()highWaterMark + drain event flow control
Iterator / Lazy EvalNode.jslib/internal/streams/Async iterators for streams — for await (const chunk of stream)
Retry BackoffNode.jsdns, httpDNS resolution retry with exponential backoff
Dependency Graphpnpmgraph-sequencerTopological sort of workspace packages for build order
Rate LimiterExpressexpress-rate-limitToken bucket middleware for API rate limiting
Circuit Breakeropossumlib/circuit.jsNode.js circuit breaker for microservice resilience
Event LoopNode.js (libuv)deps/uv/src/unix/core.cuv_run() — single-threaded I/O multiplexing via epoll/kqueue
Middleware ChainKoa.jskoa-composeCompose middleware into an onion-model pipeline with async/await

How They Compose: An HTTP Request

When an Express/Koa server handles a request, patterns compose from the network layer through to the response:

Incoming HTTP request
1
Event Loop

libuv's uv_run() picks up the socket event from epoll/kqueue. The request is dispatched to the JS callback on the main thread. No threads blocked.

2
Rate Limiter

express-rate-limit checks the token bucket. If the client exceeded their quota, respond 429 immediately.

3
Middleware Chain

Koa-compose runs middleware in onion order: auth → validate → handler → log. Each calls next() to proceed or short-circuits on error.

4
Observer

The handler emits events (e.g., 'userCreated'). EventEmitter notifies all subscribers: cache invalidation, audit log, notification service — all decoupled.

5
Backpressure

If the response streams a large file, the Writable stream applies highWaterMark. When the client reads slowly, the 'drain' event pauses the producer to prevent memory blowup.

The event loop is the foundation: everything runs on a single thread, and I/O never blocks. This is why Node.js can handle thousands of concurrent connections — each request uses patterns (middleware, observers, backpressure) rather than spawning threads.

Further Reading

Released under the MIT License.