Token导航 LogoToken导航

Agent Skill · 研究检索

nestjs-dependency-injection(Nestjs 依赖注入)

nestjs-dependency-injection 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

nestjs-dependency-injection 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描有未通过项,按安装量在研究检索类的 73,675 个 Skill 里排第 43,065。

总安装 1,014周安装 41GitHub Stars 142下载 318核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:nestjs-dependency-injection(Nestjs 依赖注入)
来源仓库:https://github.com/thebushidocollective/han
仓库路径:skills/nestjs-dependency-injection
安装命令:
npx skills add https://github.com/thebushidocollective/han --skill nestjs-dependency-injection
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/thebushidocollective/han --skill nestjs-dependency-injection

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

nestjs-dependency-injection 用于查找、检索和筛选相关信息。适合在 Codex、Claude、Cursor、Gemini CLI 中根据关键词或任务场景快速定位候选结果。通过 npx skills add 命令从指定仓库安装,需结合原始 README 核验具体用法。

安装前建议确认权限范围、维护状态及是否会触发联网、命令执行或文件读写操作。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/nestjs-dependency-injection

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/thebushidocollective/han --skill nestjs-depend…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

nestjs-dependency-injection 怎么安装?

在终端执行 npx skills add https://github.com/thebushidocollective/han --skill nestjs-dependency-injec…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

nestjs-dependency-injection 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

nestjs-dependency-injection 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

nestjs-dependency-injection 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

nestjs-dependency-injection 用的人多吗?

公开数据是累计安装 1,014 次,近一周安装 41 次,下载 318 次,GitHub 142 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

nestjs-dependency-injection 安全吗?

第三方扫描有未通过项,风险较高。除非你确认过源码,否则不建议直接安装到常用环境。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。

先想清楚的地方

  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
nestjs-dependency-injection

Nestjs 依赖注入 · 本页

1,014142需要联网MIT未通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

NestJS Dependency Injection

Master NestJS dependency injection for building modular, testable Node.js applications with proper service architecture, provider patterns, and module organization.

Table of Contents

Provider Patterns

Class Providers (Standard Pattern)

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

@Injectable()
export class UserService {
  private users: User[] = [];

  findAll(): User[] {
    return this.users;
  }

  findById(id: string): User | undefined {
    return this.users.find(user => user.id === id);
  }

  create(user: User): User {
    this.users.push(user);
    return user;
  }
}

// Module registration
@Module({
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

Value Providers

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

// Simple value provider
const DATABASE_CONNECTION = {
  provide: 'DATABASE_CONNECTION',
  useValue: {
    host: 'localhost',
    port: 5432,
    database: 'mydb',
  },
};

// Configuration value provider
const APP_CONFIG = {
  provide: 'APP_CONFIG',
  useValue: {
    apiUrl: process.env.API_URL,
    timeout: 5000,
    retries: 3,
  },
};

@Module({
  providers: [DATABASE_CONNECTION, APP_CONFIG],
  exports: [DATABASE_CONNECTION, APP_CONFIG],
})
export class ConfigModule {}

// Usage in service
@Injectable()
export class ApiService {
  constructor(
    @Inject('APP_CONFIG') private config: AppConfig,
  ) {}

  async fetchData(): Promise<any> {
    const response = await fetch(this.config.apiUrl, {
      timeout: this.config.timeout,
    });
    return response.json();
  }
}

Factory Providers

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

// Simple factory provider
const CONNECTION_FACTORY = {
  provide: 'DATABASE_CONNECTION',
  useFactory: () => {
    return createConnection({
      type: 'postgres',
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      database: process.env.DB_NAME,
    });
  },
};

// Factory with dependencies
const CACHE_MANAGER = {
  provide: 'CACHE_MANAGER',
  useFactory: (config: ConfigService) => {
    return createCacheManager({
      store: config.get('CACHE_STORE'),
      ttl: config.get('CACHE_TTL'),
      max: config.get('CACHE_MAX_ITEMS'),
    });
  },
  inject: [ConfigService],
};

@Module({
  providers: [
    ConfigService,
    CONNECTION_FACTORY,
    CACHE_MANAGER,
  ],
  exports: ['DATABASE_CONNECTION', 'CACHE_MANAGER'],
})
export class DatabaseModule {}

Async Providers with useFactory

import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

const DATABASE_PROVIDER = {
  provide: 'DATABASE_CONNECTION',
  useFactory: async (config: ConfigService) => {
    const connection = await createConnection({
      type: 'postgres',
      host: config.get('DB_HOST'),
      port: config.get('DB_PORT'),
      username: config.get('DB_USER'),
      password: config.get('DB_PASSWORD'),
      database: config.get('DB_NAME'),
    });

    await connection.runMigrations();
    return connection;
  },
  inject: [ConfigService],
};

const REDIS_PROVIDER = {
  provide: 'REDIS_CLIENT',
  useFactory: async (config: ConfigService) => {
    const client = createClient({
      url: config.get('REDIS_URL'),
    });

    await client.connect();

    client.on('error', (err) => {
      console.error('Redis error:', err);
    });

    return client;
  },
  inject: [ConfigService],
};

@Module({
  providers: [DATABASE_PROVIDER, REDIS_PROVIDER],
  exports: ['DATABASE_CONNECTION', 'REDIS_CLIENT'],
})
export class DataModule {}

Token-Based Injection with String Tokens

import { Inject, Injectable, Module } from '@nestjs/common';

// Define string tokens as constants
export const LOGGER_TOKEN = 'LOGGER';
export const METRICS_TOKEN = 'METRICS';
export const API_CLIENT_TOKEN = 'API_CLIENT';

// Provider definitions
const LOGGER_PROVIDER = {
  provide: LOGGER_TOKEN,
  useFactory: () => {
    return createLogger({
      level: process.env.LOG_LEVEL || 'info',
      format: 'json',
    });
  },
};

const METRICS_PROVIDER = {
  provide: METRICS_TOKEN,
  useValue: createMetricsClient(),
};

@Module({
  providers: [LOGGER_PROVIDER, METRICS_PROVIDER],
  exports: [LOGGER_TOKEN, METRICS_TOKEN],
})
export class ObservabilityModule {}

// Usage in service
@Injectable()
export class UserService {
  constructor(
    @Inject(LOGGER_TOKEN) private logger: Logger,
    @Inject(METRICS_TOKEN) private metrics: MetricsClient,
  ) {}

  async createUser(data: CreateUserDto): Promise<User> {
    this.logger.info('Creating user', { email: data.email });
    this.metrics.increment('user.created');

    const user = await this.repository.create(data);
    return user;
  }
}

Token-Based Injection with Symbol Tokens

import { Inject, Injectable, Module } from '@nestjs/common';

// Define symbol tokens for better type safety
export const DATABASE_CONNECTION = Symbol('DATABASE_CONNECTION');
export const CACHE_MANAGER = Symbol('CACHE_MANAGER');
export const EVENT_BUS = Symbol('EVENT_BUS');

// Provider with symbol token
const DB_PROVIDER = {
  provide: DATABASE_CONNECTION,
  useFactory: async () => {
    return await createDatabaseConnection();
  },
};

const CACHE_PROVIDER = {
  provide: CACHE_MANAGER,
  useClass: RedisCacheManager,
};

@Module({
  providers: [DB_PROVIDER, CACHE_PROVIDER],
  exports: [DATABASE_CONNECTION, CACHE_MANAGER],
})
export class InfrastructureModule {}

// Usage with symbol tokens
@Injectable()
export class ProductService {
  constructor(
    @Inject(DATABASE_CONNECTION) private db: Connection,
    @Inject(CACHE_MANAGER) private cache: CacheManager,
  ) {}

  async findById(id: string): Promise<Product> {
    const cached = await this.cache.get(`product:${id}`);
    if (cached) return cached;

    const product = await this.db
      .getRepository(Product)
      .findOne({ where: { id } });

    await this.cache.set(`product:${id}`, product, 3600);
    return product;
  }
}

Optional Dependencies with @Optional()

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class NotificationService {
  constructor(
    @Optional()
    @Inject('EMAIL_SERVICE')
    private emailService?: EmailService,
    @Optional()
    @Inject('SMS_SERVICE')
    private smsService?: SmsService,
  ) {}

  async notify(user: User, message: string): Promise<void> {
    // Gracefully handle missing optional dependencies
    if (this.emailService) {
      await this.emailService.send(user.email, message);
    }

    if (this.smsService && user.phone) {
      await this.smsService.send(user.phone, message);
    }

    // Always have a fallback notification method
    await this.logNotification(user, message);
  }

  private async logNotification(
    user: User,
    message: string,
  ): Promise<void> {
    console.log(`Notification for ${user.id}: ${message}`);
  }
}

// Module with optional providers
@Module({
  providers: [
    NotificationService,
    // EMAIL_SERVICE might not be registered
    // SMS_SERVICE might not be registered
  ],
  exports: [NotificationService],
})
export class NotificationModule {}

Property-Based Injection

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

// Property-based injection (less preferred)
@Injectable()
export class PaymentService {
  @Inject('PAYMENT_GATEWAY')
  private paymentGateway: PaymentGateway;

  @Inject('FRAUD_DETECTOR')
  private fraudDetector: FraudDetector;

  async processPayment(
    amount: number,
    card: CardDetails,
  ): Promise<PaymentResult> {
    const isFraudulent = await this.fraudDetector.check(card);
    if (isFraudulent) {
      throw new FraudDetectedException();
    }

    return await this.paymentGateway.charge(amount, card);
  }
}

// Constructor-based injection (preferred)
@Injectable()
export class OrderService {
  constructor(
    @Inject('PAYMENT_SERVICE')
    private readonly paymentService: PaymentService,
    @Inject('INVENTORY_SERVICE')
    private readonly inventoryService: InventoryService,
  ) {}

  async createOrder(data: CreateOrderDto): Promise<Order> {
    await this.inventoryService.reserve(data.items);
    await this.paymentService.processPayment(
      data.total,
      data.card,
    );

    return await this.repository.create(data);
  }
}

Class Provider with useClass

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

// Abstract interface
export abstract class LoggerService {
  abstract log(message: string): void;
  abstract error(message: string, trace: string): void;
}

// Concrete implementations
@Injectable()
export class ConsoleLoggerService extends LoggerService {
  log(message: string): void {
    console.log(message);
  }

  error(message: string, trace: string): void {
    console.error(message, trace);
  }
}

@Injectable()
export class FileLoggerService extends LoggerService {
  log(message: string): void {
    fs.appendFileSync('app.log', `${message}\n`);
  }

  error(message: string, trace: string): void {
    fs.appendFileSync('error.log', `${message}\n${trace}\n`);
  }
}

// Use different implementations based on environment
@Module({
  providers: [
    {
      provide: LoggerService,
      useClass:
        process.env.NODE_ENV === 'production'
          ? FileLoggerService
          : ConsoleLoggerService,
    },
  ],
  exports: [LoggerService],
})
export class LoggerModule {}

// Usage
@Injectable()
export class AppService {
  constructor(private readonly logger: LoggerService) {}

  doSomething(): void {
    this.logger.log('Operation completed');
  }
}

Alias Providers (useExisting)

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

@Injectable()
export class UserService {
  findAll(): User[] {
    return [];
  }
}

// Create an alias for the service
@Module({
  providers: [
    UserService,
    {
      provide: 'IUserService',
      useExisting: UserService,
    },
    {
      provide: 'UserRepository',
      useExisting: UserService,
    },
  ],
  exports: [
    UserService,
    'IUserService',
    'UserRepository',
  ],
})
export class UserModule {}

// Can inject using any of the tokens
@Injectable()
export class ReportService {
  constructor(
    @Inject('IUserService') private userService: UserService,
  ) {}

  async generateReport(): Promise<Report> {
    const users = this.userService.findAll();
    return this.buildReport(users);
  }
}

Module System

Module Organization and Encapsulation

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

// Feature module with proper encapsulation
@Module({
  imports: [DatabaseModule, CacheModule],
  providers: [
    UserService,
    UserRepository,
    UserValidator,
  ],
  controllers: [UserController],
  exports: [UserService], // Only export what's needed
})
export class UserModule {}

// Domain module grouping related features
@Module({
  imports: [
    UserModule,
    AuthModule,
    ProfileModule,
  ],
})
export class IdentityModule {}

// Application module
@Module({
  imports: [
    ConfigModule.forRoot(),
    IdentityModule,
    ProductModule,
    OrderModule,
  ],
})
export class AppModule {}

Global Modules with @Global()

import { Module, Global } from '@nestjs/common';

// Global module available everywhere
@Global()
@Module({
  providers: [
    {
      provide: 'LOGGER',
      useFactory: () => createLogger(),
    },
    {
      provide: 'CONFIG',
      useValue: loadConfiguration(),
    },
  ],
  exports: ['LOGGER', 'CONFIG'],
})
export class CoreModule {}

// Usage in any module without importing
@Injectable()
export class AnyService {
  constructor(
    @Inject('LOGGER') private logger: Logger,
    @Inject('CONFIG') private config: Config,
  ) {}
}

// Register global module once in AppModule
@Module({
  imports: [
    CoreModule, // Only import once
    FeatureModule1,
    FeatureModule2,
  ],
})
export class AppModule {}

Dynamic Modules with forRoot

import { Module, DynamicModule, Provider } from '@nestjs/common';

export interface DatabaseModuleOptions {
  host: string;
  port: number;
  username: string;
  password: string;
  database: string;
}

@Module({})
export class DatabaseModule {
  static forRoot(
    options: DatabaseModuleOptions,
  ): DynamicModule {
    const connectionProvider: Provider = {
      provide: 'DATABASE_CONNECTION',
      useFactory: async () => {
        return await createConnection(options);
      },
    };

    return {
      module: DatabaseModule,
      providers: [
        connectionProvider,
        DatabaseService,
      ],
      exports: [
        'DATABASE_CONNECTION',
        DatabaseService,
      ],
      global: true,
    };
  }
}

// Usage in AppModule
@Module({
  imports: [
    DatabaseModule.forRoot({
      host: 'localhost',
      port: 5432,
      username: 'admin',
      password: 'secret',
      database: 'myapp',
    }),
  ],
})
export class AppModule {}

Dynamic Modules with forRootAsync

import {
  Module,
  DynamicModule,
  Provider,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

export interface CacheModuleAsyncOptions {
  useFactory: (...args: any[]) => Promise<CacheOptions>;
  inject?: any[];
}

@Module({})
export class CacheModule {
  static forRootAsync(
    options: CacheModuleAsyncOptions,
  ): DynamicModule {
    const cacheOptionsProvider: Provider = {
      provide: 'CACHE_OPTIONS',
      useFactory: options.useFactory,
      inject: options.inject || [],
    };

    const cacheProvider: Provider = {
      provide: 'CACHE_MANAGER',
      useFactory: async (cacheOptions: CacheOptions) => {
        return await createCacheManager(cacheOptions);
      },
      inject: ['CACHE_OPTIONS'],
    };

    return {
      module: CacheModule,
      providers: [
        cacheOptionsProvider,
        cacheProvider,
        CacheService,
      ],
      exports: ['CACHE_MANAGER', CacheService],
      global: true,
    };
  }
}

// Usage with ConfigService
@Module({
  imports: [
    ConfigModule.forRoot(),
    CacheModule.forRootAsync({
      useFactory: async (config: ConfigService) => ({
        store: config.get('CACHE_STORE'),
        ttl: config.get('CACHE_TTL'),
        max: config.get('CACHE_MAX_ITEMS'),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Module Re-exporting

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

// Low-level infrastructure modules
@Module({
  providers: [DatabaseService],
  exports: [DatabaseService],
})
export class DatabaseModule {}

@Module({
  providers: [CacheService],
  exports: [CacheService],
})
export class CacheModule {}

@Module({
  providers: [QueueService],
  exports: [QueueService],
})
export class QueueModule {}

// Shared module that re-exports common services
@Module({
  imports: [
    DatabaseModule,
    CacheModule,
    QueueModule,
  ],
  exports: [
    DatabaseModule,
    CacheModule,
    QueueModule,
  ],
})
export class SharedModule {}

// Feature modules import SharedModule instead of individual modules
@Module({
  imports: [SharedModule],
  providers: [UserService],
  controllers: [UserController],
})
export class UserModule {}

@Module({
  imports: [SharedModule],
  providers: [ProductService],
  controllers: [ProductController],
})
export class ProductModule {}

Circular Dependencies Handling

import { Module, forwardRef } from '@nestjs/common';

// UserModule depends on AuthModule
@Module({
  imports: [forwardRef(() => AuthModule)],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

// AuthModule depends on UserModule
@Module({
  imports: [forwardRef(() => UserModule)],
  providers: [AuthService],
  exports: [AuthService],
})
export class AuthModule {}

// Service-level circular dependency
@Injectable()
export class UserService {
  constructor(
    @Inject(forwardRef(() => AuthService))
    private authService: AuthService,
  ) {}
}

@Injectable()
export class AuthService {
  constructor(
    @Inject(forwardRef(() => UserService))
    private userService: UserService,
  ) {}
}

Feature Modules with Lazy Loading

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

// Admin feature module
@Module({
  imports: [SharedModule],
  providers: [
    AdminService,
    AdminGuard,
  ],
  controllers: [AdminController],
})
export class AdminModule {}

// Lazy load admin module only when needed
@Module({
  imports: [
    // Other modules loaded eagerly
    CoreModule,
    UserModule,
  ],
})
export class AppModule {}

// In a controller or service, load AdminModule dynamically
@Injectable()
export class AppService {
  constructor(private readonly lazyModuleLoader: LazyModuleLoader) {}

  async performAdminTask(): Promise<void> {
    const moduleRef = await this.lazyModuleLoader.load(
      () => AdminModule,
    );
    const adminService = moduleRef.get(AdminService);
    await adminService.doAdminWork();
  }
}

Module Configuration Pattern

import { Module, DynamicModule } from '@nestjs/common';

export interface EmailModuleOptions {
  from: string;
  host: string;
  port: number;
  secure: boolean;
}

@Module({})
export class EmailModule {
  static forRoot(
    options: EmailModuleOptions,
  ): DynamicModule {
    return {
      module: EmailModule,
      providers: [
        {
          provide: 'EMAIL_OPTIONS',
          useValue: options,
        },
        EmailService,
      ],
      exports: [EmailService],
    };
  }

  static forFeature(): DynamicModule {
    return {
      module: EmailModule,
      providers: [EmailTemplateService],
      exports: [EmailTemplateService],
    };
  }
}

// Root module configuration
@Module({
  imports: [
    EmailModule.forRoot({
      from: 'noreply@example.com',
      host: 'smtp.example.com',
      port: 587,
      secure: true,
    }),
  ],
})
export class AppModule {}

// Feature module usage
@Module({
  imports: [EmailModule.forFeature()],
  providers: [NotificationService],
})
export class NotificationModule {}

Shared Module Pattern

import { Module, Global } from '@nestjs/common';

// Shared utilities module
@Global()
@Module({
  providers: [
    DateService,
    StringService,
    ValidationService,
  ],
  exports: [
    DateService,
    StringService,
    ValidationService,
  ],
})
export class UtilsModule {}

// Shared data access module
@Module({
  providers: [
    DataSource,
    TransactionManager,
    UnitOfWork,
  ],
  exports: [
    DataSource,
    TransactionManager,
    UnitOfWork,
  ],
})
export class DataAccessModule {}

// Combine into SharedModule
@Module({
  imports: [
    UtilsModule,
    DataAccessModule,
  ],
  exports: [
    UtilsModule,
    DataAccessModule,
  ],
})
export class SharedModule {}

Injection Scopes

DEFAULT Scope (Singleton)

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

// Default scope - single instance shared across the app
@Injectable()
export class ConfigService {
  private config: Record<string, any>;

  constructor() {
    this.config = this.loadConfiguration();
  }

  get(key: string): any {
    return this.config[key];
  }

  private loadConfiguration(): Record<string, any> {
    // Loaded once when application starts
    return {
      apiUrl: process.env.API_URL,
      dbHost: process.env.DB_HOST,
    };
  }
}

// Singleton service with state
@Injectable()
export class CacheService {
  private cache = new Map<string, any>();

  set(key: string, value: any): void {
    this.cache.set(key, value);
  }

  get(key: string): any {
    return this.cache.get(key);
  }

  // State is shared across all requests
  clear(): void {
    this.cache.clear();
  }
}

REQUEST Scope with Performance Implications

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

// Request-scoped provider - new instance per request
@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
  constructor(@Inject(REQUEST) private request: Request) {}

  getUserId(): string {
    return this.request.user?.id;
  }

  getTenantId(): string {
    return this.request.headers['x-tenant-id'] as string;
  }

  getTraceId(): string {
    return this.request.headers['x-trace-id'] as string;
  }
}

// Service that depends on request-scoped provider
@Injectable({ scope: Scope.REQUEST })
export class AuditService {
  constructor(
    private readonly context: RequestContextService,
  ) {}

  async logAction(action: string): Promise<void> {
    await this.repository.create({
      userId: this.context.getUserId(),
      tenantId: this.context.getTenantId(),
      action,
      timestamp: new Date(),
    });
  }
}

// Performance consideration - all consumers become request-scoped
@Injectable({ scope: Scope.REQUEST })
export class UserService {
  // This service is now created per request
  // because it depends on a request-scoped service
  constructor(
    private readonly audit: AuditService,
  ) {
    console.log('New UserService instance created');
  }

  async createUser(data: CreateUserDto): Promise<User> {
    const user = await this.repository.create(data);
    await this.audit.logAction('user.created');
    return user;
  }
}

TRANSIENT Scope

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

// Transient scope - new instance every time it's injected
@Injectable({ scope: Scope.TRANSIENT })
export class UniqueIdGenerator {
  private readonly id: string;

  constructor() {
    this.id = Math.random().toString(36).substring(7);
    console.log(`New generator created with id: ${this.id}`);
  }

  generate(): string {
    return `${this.id}-${Date.now()}`;
  }
}

// Each injection point gets its own instance
@Injectable()
export class OrderService {
  constructor(
    private readonly idGen1: UniqueIdGenerator, // Instance 1
  ) {}
}

@Injectable()
export class InvoiceService {
  constructor(
    private readonly idGen2: UniqueIdGenerator, // Instance 2
  ) {}
}

// Transient for stateful operations
@Injectable({ scope: Scope.TRANSIENT })
export class QueryBuilder {
  private conditions: string[] = [];
  private params: any[] = [];

  where(condition: string, ...params: any[]): this {
    this.conditions.push(condition);
    this.params.push(...params);
    return this;
  }

  build(): { query: string; params: any[] } {
    return {
      query: `SELECT * FROM table WHERE ${this.conditions.join(' AND ')}`,
      params: this.params,
    };
  }
}

Durable Providers

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

// Durable provider - survives across requests
@Injectable({ scope: Scope.DEFAULT, durable: true })
export class ConnectionPoolService {
  private pool: Pool;

  constructor() {
    this.pool = createPool({
      host: 'localhost',
      port: 5432,
      max: 20,
    });
  }

  getConnection(): Promise<Connection> {
    return this.pool.connect();
  }

  async onModuleDestroy(): Promise<void> {
    await this.pool.end();
  }
}

// Durable request-scoped provider
@Injectable({
  scope: Scope.REQUEST,
  durable: true,
})
export class RequestLoggerService {
  private logs: string[] = [];

  log(message: string): void {
    this.logs.push(message);
  }

  getLogs(): string[] {
    return this.logs;
  }
}

Scope Inheritance

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

// Parent service with DEFAULT scope
@Injectable()
export class DatabaseService {
  query(sql: string): Promise<any> {
    return this.pool.query(sql);
  }
}

// Child service inherits scope from parent
@Injectable()
export class UserRepository {
  constructor(private readonly db: DatabaseService) {}

  findAll(): Promise<User[]> {
    return this.db.query('SELECT * FROM users');
  }
}

// Request-scoped parent
@Injectable({ scope: Scope.REQUEST })
export class RequestContext {
  constructor(@Inject(REQUEST) private request: Request) {}

  getTenantId(): string {
    return this.request.headers['x-tenant-id'] as string;
  }
}

// Child inherits REQUEST scope
@Injectable({ scope: Scope.REQUEST })
export class TenantService {
  constructor(
    private readonly context: RequestContext,
    private readonly db: DatabaseService, // Still singleton
  ) {}

  async getData(): Promise<any[]> {
    const tenantId = this.context.getTenantId();
    return this.db.query(
      `SELECT * FROM data WHERE tenant_id = '${tenantId}'`,
    );
  }
}

Scope Configuration in Modules

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

@Module({
  providers: [
    // Default scope (singleton)
    ConfigService,

    // Request scope
    {
      provide: 'REQUEST_LOGGER',
      scope: Scope.REQUEST,
      useClass: RequestLoggerService,
    },

    // Transient scope
    {
      provide: 'ID_GENERATOR',
      scope: Scope.TRANSIENT,
      useClass: IdGeneratorService,
    },
  ],
})
export class AppModule {}

Advanced Patterns

Custom Decorators for Injection

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

// Custom decorator for logger injection
export const InjectLogger = () => Inject('LOGGER');

// Custom decorator for repository injection
export function InjectRepository(
  entity: Function,
): ParameterDecorator {
  return Inject(`${entity.name}Repository`);
}

// Custom decorator with options
export function InjectCache(
  namespace?: string,
): ParameterDecorator {
  const token = namespace ? `CACHE:${namespace}` : 'CACHE';
  return Inject(token);
}

// Usage in services
@Injectable()
export class UserService {
  constructor(
    @InjectLogger() private logger: Logger,
    @InjectRepository(User) private repo: Repository<User>,
    @InjectCache('users') private cache: CacheManager,
  ) {}

  async findAll(): Promise<User[]> {
    this.logger.log('Finding all users');

    const cached = await this.cache.get('all');
    if (cached) return cached;

    const users = await this.repo.find();
    await this.cache.set('all', users, 300);
    return users;
  }
}

// Register providers with custom tokens
@Module({
  providers: [
    {
      provide: 'LOGGER',
      useFactory: () => createLogger(),
    },
    {
      provide: 'UserRepository',
      useClass: UserRepository,
    },
    {
      provide: 'CACHE:users',
      useFactory: () => createCacheManager('users'),
    },
  ],
})
export class UserModule {}

Provider Arrays and Multi-Providers

import { Module, Inject } from '@nestjs/common';

// Define token for array of providers
export const EVENT_HANDLERS = 'EVENT_HANDLERS';

// Multiple implementations of event handler
@Injectable()
export class UserEventHandler implements EventHandler {
  handle(event: Event): void {
    console.log('User event:', event);
  }
}

@Injectable()
export class AuditEventHandler implements EventHandler {
  handle(event: Event): void {
    console.log('Audit event:', event);
  }
}

@Injectable()
export class NotificationEventHandler implements EventHandler {
  handle(event: Event): void {
    console.log('Notification event:', event);
  }
}

// Module registering multiple providers
@Module({
  providers: [
    UserEventHandler,
    AuditEventHandler,
    NotificationEventHandler,
    {
      provide: EVENT_HANDLERS,
      useFactory: (
        userHandler: UserEventHandler,
        auditHandler: AuditEventHandler,
        notificationHandler: NotificationEventHandler,
      ) => [userHandler, auditHandler, notificationHandler],
      inject: [
        UserEventHandler,
        AuditEventHandler,
        NotificationEventHandler,
      ],
    },
  ],
  exports: [EVENT_HANDLERS],
})
export class EventModule {}

// Service using array of providers
@Injectable()
export class EventBus {
  constructor(
    @Inject(EVENT_HANDLERS)
    private handlers: EventHandler[],
  ) {}

  emit(event: Event): void {
    this.handlers.forEach((handler) => {
      handler.handle(event);
    });
  }
}

Lazy Module Loading

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

@Injectable()
export class ReportService {
  constructor(
    private readonly lazyModuleLoader: LazyModuleLoader,
  ) {}

  async generateComplexReport(): Promise<Report> {
    // Load heavy module only when needed
    const moduleRef = await this.lazyModuleLoader.load(
      () => import('./analytics/analytics.module')
        .then((m) => m.AnalyticsModule),
    );

    const analyticsService = moduleRef.get(AnalyticsService);
    const data = await analyticsService.analyze();

    return this.buildReport(data);
  }

  async exportToPdf(report: Report): Promise<Buffer> {
    // Load PDF module lazily
    const moduleRef = await this.lazyModuleLoader.load(
      () => import('./pdf/pdf.module')
        .then((m) => m.PdfModule),
    );

    const pdfService = moduleRef.get(PdfService);
    return await pdfService.generate(report);
  }
}

Testing with Dependency Injection

import { Test, TestingModule } from '@nestjs/testing';

describe('UserService', () => {
  let service: UserService;
  let repository: Repository<User>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: 'UserRepository',
          useValue: {
            find: jest.fn(),
            findOne: jest.fn(),
            create: jest.fn(),
            save: jest.fn(),
          },
        },
        {
          provide: 'LOGGER',
          useValue: {
            log: jest.fn(),
            error: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    repository = module.get('UserRepository');
  });

  it('should find all users', async () => {
    const users = [{ id: '1', name: 'John' }];
    jest.spyOn(repository, 'find').mockResolvedValue(users);

    const result = await service.findAll();

    expect(result).toEqual(users);
    expect(repository.find).toHaveBeenCalledTimes(1);
  });

  it('should create a user', async () => {
    const createDto = { name: 'Jane', email: 'jane@example.com' };
    const user = { id: '2', ...createDto };

    jest.spyOn(repository, 'create').mockReturnValue(user);
    jest.spyOn(repository, 'save').mockResolvedValue(user);

    const result = await service.create(createDto);

    expect(result).toEqual(user);
    expect(repository.create).toHaveBeenCalledWith(createDto);
    expect(repository.save).toHaveBeenCalledWith(user);
  });
});

// Integration testing with test database
describe('UserService (integration)', () => {
  let app: INestApplication;
  let service: UserService;

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [
        DatabaseModule.forRoot({
          type: 'postgres',
          host: 'localhost',
          port: 5433,
          database: 'test',
        }),
        UserModule,
      ],
    }).compile();

    app = moduleRef.createNestApplication();
    await app.init();

    service = moduleRef.get<UserService>(UserService);
  });

  afterAll(async () => {
    await app.close();
  });

  it('should persist user to database', async () => {
    const createDto = { name: 'Integration', email: 'test@test.com' };
    const user = await service.create(createDto);

    expect(user.id).toBeDefined();
    expect(user.name).toBe(createDto.name);

    const found = await service.findById(user.id);
    expect(found).toEqual(user);
  });
});

ModuleRef for Dynamic Provider Resolution

import { Injectable, ModuleRef } from '@nestjs/core';

@Injectable()
export class DynamicService {
  constructor(private readonly moduleRef: ModuleRef) {}

  async processWithStrategy(
    strategyName: string,
    data: any,
  ): Promise<any> {
    // Dynamically resolve provider at runtime
    const strategy = this.moduleRef.get(
      `${strategyName}Strategy`,
      { strict: false },
    );

    return await strategy.process(data);
  }

  async getServiceByTenant(tenantId: string): Promise<any> {
    // Get service instance dynamically
    const token = `TenantService:${tenantId}`;
    try {
      return this.moduleRef.get(token, { strict: false });
    } catch {
      // Fallback to default service
      return this.moduleRef.get('DefaultTenantService');
    }
  }
}

// Using ModuleRef with request-scoped providers
@Injectable({ scope: Scope.REQUEST })
export class ContextAwareService {
  constructor(private readonly moduleRef: ModuleRef) {}

  async getCurrentUser(): Promise<User> {
    // Get request-scoped context
    const context = await this.moduleRef.resolve(
      RequestContextService,
    );
    const userId = context.getUserId();

    const userService = this.moduleRef.get(UserService);
    return await userService.findById(userId);
  }
}

Plugin Pattern with Dependency Injection

import { Module, DynamicModule, Type } from '@nestjs/common';

export interface Plugin {
  name: string;
  initialize(): Promise<void>;
  execute(data: any): Promise<any>;
}

export interface PluginModuleOptions {
  plugins: Type<Plugin>[];
}

@Module({})
export class PluginModule {
  static forRoot(
    options: PluginModuleOptions,
  ): DynamicModule {
    const pluginProviders = options.plugins.map((plugin) => ({
      provide: plugin,
      useClass: plugin,
    }));

    const pluginRegistryProvider = {
      provide: 'PLUGIN_REGISTRY',
      useFactory: (...plugins: Plugin[]) => plugins,
      inject: options.plugins,
    };

    return {
      module: PluginModule,
      providers: [
        ...pluginProviders,
        pluginRegistryProvider,
        PluginExecutor,
      ],
      exports: [PluginExecutor],
    };
  }
}

// Plugin implementations
@Injectable()
export class ValidationPlugin implements Plugin {
  name = 'validation';

  async initialize(): Promise<void> {
    console.log('Validation plugin initialized');
  }

  async execute(data: any): Promise<any> {
    // Validate data
    return data;
  }
}

@Injectable()
export class TransformPlugin implements Plugin {
  name = 'transform';

  async initialize(): Promise<void> {
    console.log('Transform plugin initialized');
  }

  async execute(data: any): Promise<any> {
    // Transform data
    return data;
  }
}

// Plugin executor
@Injectable()
export class PluginExecutor {
  constructor(
    @Inject('PLUGIN_REGISTRY')
    private plugins: Plugin[],
  ) {}

  async executeAll(data: any): Promise<any> {
    let result = data;
    for (const plugin of this.plugins) {
      result = await plugin.execute(result);
    }
    return result;
  }
}

// Usage
@Module({
  imports: [
    PluginModule.forRoot({
      plugins: [ValidationPlugin, TransformPlugin],
    }),
  ],
})
export class AppModule {}

Conditional Provider Registration

import { Module, DynamicModule } from '@nestjs/common';

@Module({})
export class StorageModule {
  static forRoot(): DynamicModule {
    const providers = [];

    // Conditional provider based on environment
    if (process.env.STORAGE_TYPE === 's3') {
      providers.push({
        provide: 'STORAGE_SERVICE',
        useClass: S3StorageService,
      });
    } else if (process.env.STORAGE_TYPE === 'gcs') {
      providers.push({
        provide: 'STORAGE_SERVICE',
        useClass: GcsStorageService,
      });
    } else {
      providers.push({
        provide: 'STORAGE_SERVICE',
        useClass: LocalStorageService,
      });
    }

    // Conditional feature providers
    if (process.env.ENABLE_COMPRESSION === 'true') {
      providers.push(CompressionService);
    }

    if (process.env.ENABLE_ENCRYPTION === 'true') {
      providers.push(EncryptionService);
    }

    return {
      module: StorageModule,
      providers,
      exports: ['STORAGE_SERVICE'],
    };
  }
}

Best Practices

  1. Use constructor injection over property injection: Constructor injection makes dependencies explicit, ensures they're available when the class is instantiated, and works better with TypeScript's type system.
  2. Prefer class-based providers for services: Class providers are more idiomatic in NestJS, provide better type safety, and integrate seamlessly with decorators like @Injectable().
  3. Use factory providers for complex initialization: When providers need async initialization, depend on other services, or require conditional logic, factory providers offer the flexibility needed.
  4. Avoid circular dependencies with forwardRef: While forwardRef() solves circular dependencies, it's better to restructure your modules to eliminate the circular reference entirely.
  5. Keep modules focused and cohesive: Each module should represent a single feature or domain. This improves maintainability, makes testing easier, and enables better code organization.
  6. Use dynamic modules for configurable features: When building reusable modules that need configuration, implement forRoot() and forRootAsync() methods to provide flexible initialization.
  7. Leverage REQUEST scope only when needed: Request-scoped providers have performance overhead. Use them only when you truly need per-request state, like request context or tenant isolation.
  8. Use symbol tokens for better type safety: Symbol tokens prevent naming conflicts and provide better IntelliSense support compared to string tokens.
  9. Export only what's needed from modules: Keep module interfaces minimal by exporting only the providers that other modules need to use. This maintains encapsulation and reduces coupling.
  10. Test providers in isolation: Write unit tests that mock dependencies to test providers in isolation. Use integration tests to verify the full dependency graph works correctly.

Common Pitfalls

  1. Circular dependency errors: Occurs when Module A imports Module B, and Module B imports Module A. Restructure your modules or use forwardRef() as a last resort.
  2. REQUEST scope performance overhead: Request-scoped providers are created for every request, which adds memory and CPU overhead. All dependent providers also become request-scoped.
  3. Not handling async provider initialization: Forgetting to use async/await in factory providers can lead to providers being injected before they're fully initialized.
  4. Overusing global modules: Global modules are convenient but can lead to tight coupling. Use them sparingly for truly global services like logging and configuration.
  5. Missing provider exports in modules: If a provider is not listed in the module's exports array, it won't be available to other modules that import it.
  6. Token name conflicts: Using generic string tokens like 'config' or 'service' across multiple modules can cause conflicts. Use descriptive, namespaced tokens.
  7. Memory leaks with REQUEST scope: Request-scoped providers that hold references to large objects or don't clean up resources can cause memory leaks over time.
  8. Not cleaning up resources in onModuleDestroy: Providers that create connections, timers, or other resources should implement onModuleDestroy to clean up properly.
  9. Tight coupling between modules: Importing too many modules or depending on internal implementation details creates tight coupling that makes refactoring difficult.
  10. Missing @Injectable() decorator: Forgetting to add @Injectable() to a class that should be a provider results in runtime errors when NestJS tries to inject it.

Resources

平台分布

Claude Code

28.3%
按下载量换算90

Antigravity

22.13%
按下载量换算70

OpenCode

17.08%
按下载量换算54

Codex

11.19%
按下载量换算36

Cursor

8.17%
按下载量换算26

Gemini CLI

2.9%
按下载量换算9

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。

继续浏览同类 Skills