cls-hooked

Continuation-local storage for Node.js, built on async_hooks instead of the deprecated async-listener.

Library
npm
v4.2.2
776stars
BSD-2-Clause

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity0
Maintenance20
Community72
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
50/100Fair
Architecture60
Code Quality55
Innovation40
Learning Curve45

cls-hooked is a continuation-local storage (CLS) implementation for Node.js — a fork of the original node-continuation-local-storage that replaces the deprecated async-listener mechanism with Node’s async_hooks (or AsyncWrap on older Node versions). It lets you create a namespace, set key/value context inside a ns.run() or ns.bind() callback, and read that same context anywhere further down the async call chain — across callbacks, promises, and event emitters — without threading extra parameters through every function signature.

This is the same pattern Node’s built-in AsyncLocalStorage (added in Node 12.17) now covers natively, but cls-hooked remains widely depended on by older codebases and libraries (loggers, tracers, ORMs) that adopted it before AsyncLocalStorage existed.

What You Get

  • createNamespace/getNamespace/destroyNamespace API for managing named context stores
  • ns.run()/ns.runAndReturn() to start a context scope and ns.set()/ns.get() to read and write values within it
  • ns.bind() and emitter-aware wrapping (via emitter-listener) so context survives across EventEmitter-based callbacks
  • A context-legacy.js fallback path for Node versions before async_hooks was available
  • Widespread use as a dependency inside request-logging, tracing, and ORM libraries that need implicit per-request context

Common Use Cases

  • Attaching a request ID or user context to every log line emitted during an HTTP request’s lifecycle
  • Propagating a database transaction handle through nested async calls without passing it as an explicit argument
  • Implementing APM/tracing libraries that need to correlate spans across asynchronous boundaries
  • Maintaining per-request Express/Koa middleware context across callback-based and promise-based code in legacy codebases

Under The Hood

Architecture The package exposes a Namespace constructor (in context.js) whose run()/bind() methods snapshot and restore an active context object around a callback’s execution, driven by async_hooks.createHook to enter/exit context on the init/before/after/destroy lifecycle of every async resource; context.js is the async_hooks-based implementation (Node 8.2.1+), while context-legacy.js provides an AsyncWrap-based fallback (index.js selects between them based on process.version) for Node 4.7–8.

Tech Stack Plain CommonJS JavaScript with no build step, depending on async-hook-jl (a userland async_hooks shim used pre-8.2.1) and emitter-listener (to wrap EventEmitter instances so bound context survives emitted events); tested with Mocha and Node’s tap runner.

Code Quality The repo has real test coverage under test/ (http-events, async-context, net-events, namespaces, multiple-values, and more), exercising context propagation across HTTP, net sockets, and nested namespaces — meaningful given how easy async-context bugs are to introduce silently. The code favors verbose inline debug logging (guarded by a DEBUG_CLS_HOOKED env var) over abstraction, and hasn’t seen a tagged release since 2017 despite later commits, so the published npm version lags the repository’s HEAD.

API Design The API surface (createNamespace/getNamespace, ns.run/ns.set/ns.get/ns.bind) mirrors the original continuation-local-storage package closely for drop-in compatibility, but the run/bind/context-restoration model requires understanding Node’s async execution model to use correctly, and the project’s README recommends migrating to Node’s built-in AsyncLocalStorage for new code — cls-hooked’s value today is mainly compatibility with libraries already built against it.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search