Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问clear审计异常

nestjs-dependency-injectionNestjs 依赖注入

Agent Skill

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

总安装

1,014

周安装

41

GitHub Stars

142

下载量

318
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 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

简介

nestjs-dependency-injection 用于查找、检索和筛选相关信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中根据关键词或任务场景快速定位候选结果。
  • 通过 npx skills add 命令从指定仓库安装,需结合原始 README 核验具体用法。
  • 安装前建议确认权限范围、维护状态及是否会触发联网、命令执行或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

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

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

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 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。

来源信息

继续浏览同类 Skills