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 🙏

© 2025 – Pkg Stats / Ryan Hefner

nestjs-abort-controller

v1.2.0

Published

AbortController integration for NestJS: request-scoped cancellation using AbortSignal.

Readme

nestjs-abort-controller

npm version License: MIT

Graceful request cancellation support for NestJS using native AbortController. Automatically handles client disconnections and provides easy-to-use decorators for request-scoped cancellation.

✨ Features

  • 🚀 Automatic AbortController Creation: Creates AbortSignal for each incoming HTTP request
  • 🔄 Client Disconnection Handling: Automatically aborts long-running operations when client disconnects
  • 🎯 Easy Integration: Simple decorator-based approach with @NestAbortSignal()
  • Lightweight: Zero additional dependencies (only Node >= 16)
  • 🔧 TypeScript Support: Full TypeScript support with proper type definitions
  • 🛡️ Error Handling: Built-in utilities for checking abort status
  • Configurable Timeout: Set request timeout in milliseconds to automatically abort long-running operations
  • 📝 Logging Support: Optional logging for debugging abort scenarios

📦 Installation

npm install nestjs-abort-controller

🚀 Quick Start

1. Import the Module

import { Module } from '@nestjs/common';
import { AbortControllerModule } from 'nestjs-abort-controller';

@Module({
  imports: [
    AbortControllerModule.forRoot({
      timeout: 30000, // 30000ms = 30 seconds timeout
      enableLogging: true,
    }),
  ],
})
export class AppModule {}

2. Use the Decorator

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal, throwIfAborted } from 'nestjs-abort-controller';

@Controller('api')
export class ApiController {
  @Get('long-running-operation')
  async longRunningOperation(@NestAbortSignal() signal: AbortSignal): Promise<string> {
    for (let i = 0; i < 100; i++) {
      // Check if operation was aborted
      throwIfAborted(signal);

      // Your long-running work here
      await new Promise((resolve) => setTimeout(resolve, 100));
    }

    return 'Operation completed successfully!';
  }
}

📚 API Reference

Decorators

@NestAbortSignal()

A parameter decorator that injects the AbortSignal for the current request.

@Get()
async handler(@NestAbortSignal() signal: AbortSignal) {
  // signal is automatically aborted when client disconnects
}

Utility Functions

throwIfAborted(signal: AbortSignal, message?: string): void

Throws an AbortError if the signal is aborted.

import { throwIfAborted } from 'nestjs-abort-controller';

async function longRunningOperation(signal: AbortSignal) {
  for (let i = 0; i < 1000; i++) {
    throwIfAborted(signal); // Will throw if aborted

    // Your work here
    await someAsyncWork();
  }
}

AbortError

Custom error class for abort operations.

import { AbortError } from 'nestjs-abort-controller';

try {
  // Some operation
} catch (error) {
  if (error instanceof AbortError) {
    console.log('Operation was aborted');
  }
}

AbortControllerModule

The main module that sets up the middleware for all routes.

forRoot(options?: AbortControllerOptions)

Configure the module for all routes.

import { AbortControllerModule } from 'nestjs-abort-controller';

@Module({
  imports: [
    AbortControllerModule.forRoot({
      timeout: 30000, // 30000ms = 30 seconds
      enableLogging: true,
    }),
  ],
})
export class AppModule {}

forRoutes(routes: string | string[], options?: AbortControllerOptions)

Configure the module for specific routes.

import { AbortControllerModule } from 'nestjs-abort-controller';

@Module({
  imports: [
    AbortControllerModule.forRoutes(['/api/*'], {
      timeout: 60000, // 60000ms = 60 seconds
      enableLogging: false,
    }),
  ],
})
export class AppModule {}

Configuration Options

AbortControllerOptions

interface AbortControllerOptions {
  timeout?: number; // Timeout duration in milliseconds (default: 30000ms = 30 seconds)
  enableLogging?: boolean; // Enable debug logging (default: false)
}

🔧 Advanced Usage

Using with Services

The recommended approach is to pass the AbortSignal from controller to service methods.

import { Injectable } from '@nestjs/common';
import { throwIfAborted } from 'nestjs-abort-controller';

@Injectable()
export class UserService {
  async getUsers(signal: AbortSignal) {
    for (let i = 0; i < 100; i++) {
      throwIfAborted(signal);
      await someAsyncWork();
    }
    return users;
  }
}

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal } from 'nestjs-abort-controller';

@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}

  @Get()
  getUsers(@NestAbortSignal() signal: AbortSignal) {
    // Pass signal to service
    return this.userService.getUsers(signal);
  }
}

Integration with External APIs

import { Injectable } from '@nestjs/common';

@Injectable()
export class ExternalApiService {
  async fetchFromExternalApi(signal: AbortSignal) {
    const response = await fetch('https://api.example.com/data', {
      signal, // Pass the signal to fetch
    });

    return response.json();
  }
}

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal } from 'nestjs-abort-controller';

@Controller('api')
export class ApiController {
  constructor(private externalApiService: ExternalApiService) {}

  @Get('external-data')
  async getExternalData(@NestAbortSignal() signal: AbortSignal) {
    return this.externalApiService.fetchFromExternalApi(signal);
  }
}

Manual Abort Signal Checking

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal, throwIfAborted } from 'nestjs-abort-controller';

@Controller('api')
export class ApiController {
  @Get('manual-check')
  async manualCheck(@NestAbortSignal() signal: AbortSignal) {
    for (let i = 0; i < 1000; i++) {
      // Manual check
      if (signal.aborted) {
        throw new Error('Operation was cancelled');
      }

      // Or use utility function
      throwIfAborted(signal);

      await someAsyncWork();
    }
  }
}

Error Handling

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal, throwIfAborted, AbortError } from 'nestjs-abort-controller';

@Controller('api')
export class ApiController {
  @Get('with-error-handling')
  async withErrorHandling(@NestAbortSignal() signal: AbortSignal) {
    try {
      for (let i = 0; i < 100; i++) {
        throwIfAborted(signal);
        await someAsyncWork();
      }
      return 'Success';
    } catch (error) {
      if (error instanceof AbortError) {
        // Handle abort specifically
        return 'Operation was cancelled';
      }
      throw error;
    }
  }
}

Multiple Service Calls

import { Injectable } from '@nestjs/common';
import { throwIfAborted } from 'nestjs-abort-controller';

@Injectable()
export class DataService {
  async processData(signal: AbortSignal) {
    throwIfAborted(signal);
    // Process data
    return processedData;
  }
}

@Injectable()
export class CacheService {
  async getFromCache(signal: AbortSignal) {
    throwIfAborted(signal);
    // Get from cache
    return cachedData;
  }
}

import { Controller, Get } from '@nestjs/common';
import { NestAbortSignal } from 'nestjs-abort-controller';

@Controller('data')
export class DataController {
  constructor(
    private dataService: DataService,
    private cacheService: CacheService
  ) {}

  @Get('process')
  async processData(@NestAbortSignal() signal: AbortSignal) {
    // Use same signal for multiple service calls
    const cached = await this.cacheService.getFromCache(signal);
    const processed = await this.dataService.processData(signal);

    return { cached, processed };
  }
}

🧪 Testing

import { Test, TestingModule } from '@nestjs/testing';
import { AbortControllerModule } from 'nestjs-abort-controller';

describe('AbortController', () => {
  let module: TestingModule;

  beforeEach(async () => {
    module = await Test.createTestingModule({
      imports: [AbortControllerModule.forRoot()],
    }).compile();
  });

  // Your tests here
});

📋 Requirements

  • Node.js >= 16.0.0
  • NestJS >= 8.0.0

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Built for the NestJS community
  • Inspired by the need for better request cancellation handling
  • Uses native Node.js AbortController for maximum compatibility

Made with ❤️ for the NestJS community