Claude
Skills
Sign in
Back

nestjs-dependency-injection

Included with Lifetime
$97 forever

Use when nestJS dependency injection with providers, modules, and decorators. Use when building modular NestJS applications.

General

What this skill does


# 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](#provider-patterns)
- [Module System](#module-system)
- [Injection Scopes](#injection-scopes)
- [Advanced Patterns](#advanced-patterns)
- [Best Practices](#best-practices)
- [Common Pitfalls](#common-pitfalls)
- [Resources](#resources)

## Provider Patterns

### Class Providers (Standard Pattern)

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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()

```typescript
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

```typescript
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

Related in General