# Providers and dependency injection

> Source: https://learn-nestjs.com/providers-and-di/
> Part of Learn NestJS, free to read.

Dependency injection is Nest's core mechanism. You declare what a class needs; Nest works out how to build it.

```ts
@Injectable()
export class OrdersService {
  constructor(
    private readonly users: UsersService,
    private readonly prisma: PrismaService,
  ) {}
}
```

Nest reads the parameter types at runtime (via `emitDecoratorMetadata`), looks up a provider for each, constructs them if they do not exist yet, and passes them in. There is no `new` anywhere.

The payoff is testing:

```ts
const module = await Test.createTestingModule({
  providers: [
    OrdersService,
    { provide: UsersService, useValue: { findOne: jest.fn() } },
    { provide: PrismaService, useValue: prismaMock },
  ],
}).compile();
```

Swapping a real dependency for a fake is a one-line change, because nothing ever hardcoded the dependency.

## The four provider forms

```ts
// 1. class — the common case
providers: [UsersService]

// 2. useClass — swap the implementation behind a token
providers: [{ provide: MailerService, useClass: process.env.NODE_ENV === 'test' ? FakeMailer : SesMailer }]

// 3. useValue — a constant or a mock
providers: [{ provide: 'CONFIG', useValue: { retries: 3 } }]

// 4. useFactory — computed, possibly async, may inject
providers: [{
  provide: 'DB',
  useFactory: async (config: ConfigService) => createPool(config.get('DATABASE_URL')),
  inject: [ConfigService],
}]
```

## String tokens and how to avoid them

Interfaces do not exist at runtime, so you cannot inject by interface. The workaround is a token — but use a typed constant, not a bare string.

```ts src/mail/mail.tokens.ts
export const MAILER = Symbol('MAILER');

export interface Mailer {
  send(to: string, subject: string, body: string): Promise<void>;
}
```

```ts
providers: [{ provide: MAILER, useClass: SesMailer }]

constructor(@Inject(MAILER) private readonly mailer: Mailer) {}
```

A `Symbol` cannot collide and cannot be typo'd into a different provider.

## Scopes

Providers are singletons by default, and that is almost always what you want.

```ts
@Injectable({ scope: Scope.REQUEST })     // new instance per request
export class RequestContext {}
```

:::warn Request scope is contagious and slow
Anything that injects a request-scoped provider becomes request-scoped too, all the way up the graph — including your controller. On a hot path that means constructing a chain of objects per request. Prefer `AsyncLocalStorage` for request context, or `nestjs-cls`.
:::

## Optional and circular

```ts
constructor(@Optional() private readonly cache?: CacheService) {}

// two services that need each other
constructor(@Inject(forwardRef(() => OrdersService)) private orders: OrdersService) {}
```

As with modules, `forwardRef` between *services* usually means a third thing wants extracting.

## Common questions

### Why can I not inject an interface?

TypeScript interfaces are erased at compile time, so there is nothing at runtime for Nest to look up. Use a `Symbol` token with `@Inject()`, and keep the interface for the type annotation — you get both the abstraction and working injection.

### When should I use `useFactory`?

When constructing the provider needs async work or configuration — a database pool, an SDK client built from environment values. For a plain class with injectable dependencies, the class form is simpler and does the same thing.

### Does dependency injection slow things down?

Only at startup, and imperceptibly: the graph is constructed once. The exception is request-scoped providers, which construct per request and can be measurably expensive on a hot path.
