level-read-stream
Node.js readable streams for iterating keys, values, and entries in abstract-level databases.
Repository Health
Technical Analysis
level-read-stream wraps the iterator, keys, and values methods of any abstract-level compatible database in familiar Node.js Readable streams. It ships three focused classes — EntryStream, KeyStream, and ValueStream — so you can pipe database reads directly into standard stream pipelines, async iterators, or third-party stream libraries without hand-rolling iterator-to-stream glue.
Built by the Level collective (maintainers of classic-level, memory-level, and the broader abstract-level ecosystem), the package is a thin, well-tested compatibility layer: it batches reads via nextv() for throughput, respects backpressure through highWaterMark, and closes the underlying iterator automatically on stream end or destroy.
What You Get
- EntryStream, KeyStream, and ValueStream classes for reading entries, keys, or values respectively
- Automatic backpressure handling via a configurable highWaterMark buffer (default 1000)
- Batched reads through the underlying iterator’s nextv() for better throughput than one-at-a-time iteration
- Automatic iterator cleanup — the underlying iterator is closed when the stream ends or is destroyed
- First-class TypeScript definitions with generics for key, value, and database types
Common Use Cases
- Streaming a full database export to a file or HTTP response using pipeline()
- Building ETL jobs that read from a classic-level or memory-level database and transform records in flight
- Feeding database contents into existing Node.js stream-based tooling such as CSV writers or compression streams
- Reading only keys or only values when the full entry isn’t needed, to reduce memory and I/O overhead
Under The Hood
Architecture — The package centers on a single internal base class, LevelReadStream, that extends readable-stream’s Readable in object mode. Its constructor calls db[method](options) (method being ‘iterator’, ‘keys’, or ‘values’) to create an abstract-level iterator, then implements _read(size) by calling the iterator’s nextv(size) to pull a batch of items and push them onto the stream buffer — falling back to a legacy callback-style nextv for older abstract-level versions via the internal kNextvLegacy handler. EntryStream, KeyStream, and ValueStream are thin subclasses that each call the base constructor with a different underlying method and, in EntryStream’s case, reshape [key, value] tuples into { key, value } objects. _destroy(err, callback) closes the iterator before the stream fully tears down, guaranteeing no dangling database handles.
Tech Stack — The only runtime dependency is readable-stream (a userland, browser-compatible implementation of Node’s stream primitives, kept in sync with the ecosystem so the package works consistently across Node versions and bundlers). abstract-level itself is an optional peer dependency, since the package only needs a db argument shaped like an AbstractLevel instance at runtime. TypeScript consumers additionally need @types/readable-stream. The package targets Node >=18 and ships as CommonJS with a hand-written index.d.ts rather than being authored in TypeScript directly.
Code Quality — test.js uses tape against a memory-level database, covering EntryStream/KeyStream/ValueStream output correctness, destroy behavior, and internal event-listener hygiene (via secret-event-listener, which detects listeners left attached after teardown). Linting is enforced with standard, coverage is tracked via nyc/codecov, and docs are checked with hallmark — a disciplined CI setup for a package this small. The source itself is compact (about 120 lines across index.js) and uses Symbols for private internal state, avoiding property collisions with subclasses.
API Design — The public surface is deliberately minimal: three constructors that each take a db and an options object, all of which behave exactly like a standard Node.js Readable — so they compose with pipe(), pipeline(), and for-await-of without any library-specific glue code. The split between EntryStream/KeyStream/ValueStream lets callers opt out of fetching data they don’t need (e.g. KeyStream avoids value lookups), which is a meaningful performance lever on large scans. TypeScript generics on key/value/database types make the streams type-safe for consumers using typed abstract-level implementations, and the README documents both the modern async and legacy callback iterator paths for backward compatibility.