node-pool

Promise-based generic resource pool for reusing or throttling expensive resources like database connections.

Library
npm
v3.9.0
2,407stars
MIT License

Repository Health

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

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture78
Code Quality68
Innovation75
Learning Curve35

generic-pool (published on npm from the node-pool repository) is a Promise-based object pool for Node.js. Rather than tying itself to any specific resource type, it accepts a factory object with create, destroy, and optional validate functions, letting callers pool database connections, sockets, worker handles, or any other expensive-to-create object behind a uniform acquire/release API.

Internally the pool tracks resources through a small state machine (allocated, idle, invalid, returning, validation), backed by a doubly linked list deque for available resources and a priority queue for waiting borrowers. This gives it features that hand-rolled pooling code usually lacks: priority-based queueing for waiters, an idle-object evictor that can retire stale resources on a timer, configurable min/max pool sizing, and acquire/destroy timeouts.

The library has been a de facto standard in the Node ecosystem since 2010, widely used indirectly through database drivers and ORMs (such as Knex.js and Sequelize) that need connection pooling but don’t want to reimplement it themselves.

What You Get

  • createPool(factory, opts) constructor that wraps any create/destroy/validate factory into a managed pool
  • Promise-based acquire() / release() / destroy() API plus a use(fn) helper that acquires, runs a callback, and automatically releases or destroys the resource based on outcome
  • Priority queueing so borrowers can specify a relative priority when the pool is exhausted and callers must wait
  • Configurable min/max pool size, acquireTimeoutMillis / destroyTimeoutMillis, and maxWaitingClients to bound queue growth
  • Idle-object evictor that runs on a configurable interval to destroy resources that have sat unused past idleTimeoutMillis or softIdleTimeoutMillis
  • Shipped TypeScript type definitions (index.d.ts) for typed factories and pool instances without a separate @types package

Common Use Cases

  • Pooling database client connections behind a driver or ORM that doesn’t ship its own pooling (many Knex.js and Sequelize dialect drivers use generic-pool internally)
  • Throttling concurrent access to a rate-limited external API or headless-browser instance by capping max and queueing excess requests
  • Reusing worker processes or expensive native handles (e.g. image-processing workers) instead of spawning one per request
  • Building a custom resource pool for an in-house protocol client where no existing pooling library fits the resource’s lifecycle

Under The Hood

Architecture generic-pool is organized as a set of small, single-responsibility classes wired together by lib/Pool.js, an EventEmitter subclass that owns the pool’s lifecycle. PoolOptions validates and normalizes constructor options, factoryValidator enforces the required create/destroy shape before the pool will start, and pool state is tracked across five explicit stages (ALLOCATED, IDLE, INVALID, RETURNING, VALIDATION) defined in PooledResourceStateEnum. Available resources live in a Deque (backed by DoublyLinkedList/DoublyLinkedListIterator) so the pool can operate as either a FIFO queue or a LIFO stack depending on the fifo option, while waiting borrowers sit in a PriorityQueue that fans out into per-priority sub-queues. ResourceRequest and ResourceLoan (both built on a shared Deferred promise wrapper) represent, respectively, a pending acquire and an outstanding loan, and index.js assembles the whole thing by injecting the evictor, deque, and priority-queue classes into Pool’s constructor rather than hardcoding them — a seam that exists mainly to keep the core testable in isolation. The result is a compact but genuinely layered design: no single file exceeds a few hundred lines, and swapping the eviction policy or queue strategy doesn’t require touching the acquire/release logic.

Tech Stack The library is plain CommonJS JavaScript with zero runtime dependencies, targeting Node >= 4 per package.json’s engines field, and ships hand-written TypeScript declarations (index.d.ts) validated via tsc --noEmit against a strict: false tsconfig.json. Tooling is intentionally minimal: ESLint with eslint-plugin-prettier enforces formatting as a lint error, and a Makefile drives make install/make lint/make test for both local development and the (now largely dormant) Travis CI matrix that tested against Node 6 through 11 on amd64 and ppc64le. There is no bundler, build step, or transpilation — the published package is the source as-is.

Code Quality Tests run under tap against multiple focused suites (generic-pool-test.js, dedicated acquire/destroy-timeout tests, a doubly-linked-list test, a resource-request test, and a regression test tied to a specific GitHub issue), exercising acquire/release ordering, priority behavior, and timeout edge cases directly against the public API rather than through mocks. Several older tests are left commented out in place with explanatory notes rather than deleted, which is a minor rough edge but doesn’t affect what actually runs. Error handling is explicit throughout Pool.js — factory promise rejections are funneled through named factoryCreateError/factoryDestroyError events rather than swallowed, and public methods reject their returned Promises with typed errors from lib/errors.js (TimeoutError, etc.) instead of throwing generic ones. Naming is consistent and JSDoc comments annotate most public methods and constructor parameters, though the project’s own activity_status shows very low recent commit and release activity, meaning newer Node/V8 idioms aren’t reflected in the code.

API Design The public surface is deliberately small: one factory contract (create/destroy/optional validate), one createPool entry point, and a handful of methods (acquire, release, destroy, use, start, ready, drain, clear). The use(fn) helper in particular removes the most common source of pooling bugs — forgetting to release or destroy a borrowed resource — by handling acquire/release/destroy automatically based on whether the supplied callback’s promise resolves or rejects. Priority queueing and eviction are opt-in via configuration rather than separate APIs, keeping the common case (just call acquire()/release()) free of ceremony, and the shipped .d.ts file means TypeScript consumers get typed factories and pool instances without any extra setup.

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