npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@backendkit-labs/bulkhead

v0.2.1

Published

Bulkhead concurrency limiting for Node.js — inspired by Resilience4j, with optional NestJS integration

Downloads

814

Readme

@backendkit-labs/bulkhead

npm version CI License Node Docs

Bulkhead concurrency limiting for Node.js — inspired by Resilience4j. Framework-agnostic core with optional NestJS integration.

Prevents resource exhaustion and cascading failures by limiting how many operations run simultaneously on a given resource.


Minimal Example

Self-contained runnable example — no NestJS, one file, realistic scenario.

git clone https://github.com/BackendKit-labs/backendkit-monorepo.git
cd backendkit-monorepo/examples/minimal-bulkhead
npm install && npm start

Shows Promise.all (16 concurrent) vs bulkhead (max 3 concurrent) processing the same 16 tasks side by side — with real timing output and final metrics. → full source


Installation

npm install @backendkit-labs/bulkhead

TypeScript Configuration

Subpath exports (/nestjs)

This package uses the exports field in package.json to expose the /nestjs subpath. TypeScript's ability to resolve it depends on the moduleResolution setting in your tsconfig.json.

Modern resolution (recommended) — no extra config needed:

{
  "compilerOptions": {
    "moduleResolution": "bundler"
  }
}

"bundler", "node16", and "nodenext" all understand the exports field natively. This is the recommended setting for any project using a bundler or NestJS on TypeScript ≥ 5.

Legacy resolution ("node") — add a paths alias:

NestJS projects generated before ~2024 default to "moduleResolution": "node", which ignores the exports field. Add an explicit alias so TypeScript can find the types:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "paths": {
      "@backendkit-labs/bulkhead/nestjs": [
        "./node_modules/@backendkit-labs/bulkhead/dist/nestjs/index"
      ]
    }
  }
}

Why? The "node" resolver was designed before subpath exports existed and only reads main/types at the package root — it ignores the exports map entirely. The paths alias manually points TypeScript to the correct .d.ts file.

NestJS decorator support

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

And import reflect-metadata once at application startup:

// main.ts
import 'reflect-metadata';

NestJS CLI scaffolds these automatically. You only need to verify them if setting up a project manually.


Quick Start — Framework-agnostic

import { Bulkhead } from '@backendkit-labs/bulkhead';

const bulkhead = new Bulkhead({
  name: 'payments',
  maxConcurrentCalls: 10,
  maxQueueSize: 50,
  queueTimeoutMs: 5000,
  rejectWhenFull: true,
});

const result = await bulkhead.execute(() => callPaymentApi());

Core API

Bulkhead

const bulkhead = new Bulkhead(config);

// Execute a task — waits in queue if at capacity
await bulkhead.execute(async () => { ... });

// Check if capacity is available before executing
if (bulkhead.canAccept()) { ... }

// Current metrics snapshot
const metrics = bulkhead.getMetrics();

// Reset all counters
bulkhead.resetMetrics();

BulkheadConfig

| Property | Type | Description | |----------|------|-------------| | name | string | Identifier for metrics and error messages | | maxConcurrentCalls | number | Max simultaneous executions | | maxQueueSize | number | Max tasks waiting in queue | | queueTimeoutMs | number | Max time a task can wait in queue (ms) | | rejectWhenFull | boolean | Throw immediately when full; if false, retries with exponential backoff |

BulkheadMetrics

{
  name: string;
  activeCalls: number;
  queuedCalls: number;
  maxConcurrentCalls: number;
  maxQueueSize: number;
  totalCalls: number;
  successfulCalls: number;
  failedCalls: number;
  rejectedCalls: number;
  timedOutCalls: number;
  averageDurationMs: number;
}

Errors

import { BulkheadRejectedError, BulkheadTimeoutError } from '@backendkit-labs/bulkhead';

try {
  await bulkhead.execute(task);
} catch (error) {
  if (error instanceof BulkheadRejectedError) {
    // Queue was full — task was not queued
  }
  if (error instanceof BulkheadTimeoutError) {
    // Task waited too long in queue
  }
}

BulkheadRegistry

Manages named bulkhead instances with sensible defaults for common resource types:

import { BulkheadRegistry } from '@backendkit-labs/bulkhead';

const registry = new BulkheadRegistry();

// Custom
const bh = registry.getOrCreate({ name: 'my-service', maxConcurrentCalls: 15 });

// Pre-configured factory methods
const clientBh   = registry.getForClient('client-123', '/api/orders');   // 5 concurrent, 20 queued
const serviceBh  = registry.getForService('inventory-service');           // 20 concurrent, 200 queued
const dbBh       = registry.getForDatabase('orders_schema');              // 15 concurrent, 150 queued
const externalBh = registry.getForHttpExternal('stripe-api');             // 8 concurrent, 50 queued, 10s timeout

// Observability
const all        = registry.getAllMetrics();
const overloaded = registry.getOverloadedBulkheads(); // ≥80% active capacity
registry.resetAllMetrics();

| Method | Concurrent | Queue | Timeout | |--------|-----------|-------|---------| | getForClient(id, endpoint?) | 5 | 20 | 30s | | getForService(name) | 20 | 200 | 30s | | getForDatabase(schema) | 15 | 150 | 30s | | getForHttpExternal(name) | 8 | 50 | 10s |


NestJS Integration

npm install @backendkit-labs/bulkhead

Import BulkheadModule into your NestJS application:

import { BulkheadModule } from '@backendkit-labs/bulkhead/nestjs';

@Module({
  imports: [BulkheadModule],
})
export class AppModule {}

Guard — declarative per-route protection

import { UseBulkhead, BulkheadGuard } from '@backendkit-labs/bulkhead/nestjs';

@Controller('orders')
export class OrdersController {
  // Shared service-level limit
  @UseBulkhead({ name: 'orders-service' })
  @UseGuards(BulkheadGuard)
  @Get()
  findAll() { ... }

  // Per-client isolation (reads x-client-id header)
  @UseBulkhead({ name: 'orders-create', perClient: true })
  @UseGuards(BulkheadGuard)
  @Post()
  create() { ... }
}

Returns 503 Service Unavailable when at capacity.

Interceptor — wraps handler execution inside the bulkhead

import { BulkheadInterceptor } from '@backendkit-labs/bulkhead/nestjs';

// Apply globally
app.useGlobalInterceptors(new BulkheadInterceptor(registry));

// Or per controller / route
@UseInterceptors(BulkheadInterceptor)
@Controller('reports')
export class ReportsController { ... }

Returns 503 on rejection, 408 on timeout.

Middleware — global HTTP concurrency limit

Protects the entire service from being overwhelmed before requests even reach your handlers:

import { HttpBulkheadMiddleware } from '@backendkit-labs/bulkhead/nestjs';

@Module({ imports: [BulkheadModule] })
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(HttpBulkheadMiddleware).forRoutes('*');
  }
}

Configure via environment variables:

| Variable | Default | Description | |----------|---------|-------------| | HTTP_BULKHEAD_CONCURRENCY | 50 | Max concurrent requests | | HTTP_BULKHEAD_MAX_QUEUE | 100 | Max queued requests |

Returns 429 Too Many Requests when the queue is full.

Method Decorator

import { WithBulkhead } from '@backendkit-labs/bulkhead/nestjs';

@Injectable()
export class ReportService {
  // Must have bulkheadRegistry injected
  constructor(public readonly bulkheadRegistry: BulkheadRegistry) {}

  @WithBulkhead({ name: 'report-generation', maxConcurrent: 3 })
  async generateReport(id: string) { ... }
}

Monitoring — BulkheadService

import { BulkheadService } from '@backendkit-labs/bulkhead/nestjs';

@Controller('health')
export class HealthController {
  constructor(private readonly bulkheads: BulkheadService) {}

  @Get('bulkheads')
  getMetrics() {
    return {
      all: this.bulkheads.getAllMetrics(),
      critical: this.bulkheads.getCriticalBulkheads(), // ≥90% active
    };
  }
}

BulkheadService also logs a warning every 60 seconds when any bulkhead reaches 90%+ utilization.


Architecture

@backendkit-labs/bulkhead         (core — no framework deps)
  Bulkhead                        queue-based concurrency limiter
  BulkheadRegistry                named instances + factory methods

@backendkit-labs/bulkhead/nestjs  (optional NestJS layer)
  BulkheadModule                  NestJS module
  BulkheadGuard                   @UseBulkhead() per-route decorator
  BulkheadInterceptor             wraps handler in execute()
  HttpBulkheadMiddleware          global HTTP request limiter
  WithBulkhead                    method-level decorator
  BulkheadService                 metrics + auto-monitoring

License

Apache-2.0 — BackendKit Labs