# NestJS Providers: The Core Building Blocks of Dependency Injection

[NestJS](https://blog.bytescrum.com/introduction-to-nestjs-a-beginners-guide) is a powerful framework for building [scalable](https://bytescrum.com/) and maintainable server-side applications. One of its key features is the robust dependency injection system, which revolves around the concept of providers. In this blog post, we'll dive deep into what providers are, how they work, and how you can use them to create clean, modular, and testable code in your NestJS applications.

## What are Providers?

In NestJS, providers are the basic building blocks for dependency injection. They are responsible for creating and managing the instances of the classes they provide. Providers can be services, repositories, factories, or any other class that holds business logic or data access logic.

## Types of Providers

NestJS supports various types of providers, each serving a specific purpose:

1. **Class Providers**: The most common type, where a class is used as a provider.
    
2. **Value Providers**: Provide a static value, useful for constants or configurations.
    
3. **Factory Providers**: Use a factory function to create the provider's value.
    
4. **Existing Providers**: Alias an existing provider, useful for creating different names for the same provider.
    

### Class Providers

Class providers are the most straightforward and commonly used providers in NestJS. They are simply classes decorated with the `@Injectable()` decorator.

### Example: UserService

Let's create a simple `UserService` that provides user-related functionalities.

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

@Injectable()
export class UserService {
  private users = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Doe' },
  ];

  getUsers() {
    return this.users;
  }

  getUserById(id: number) {
    return this.users.find(user => user.id === id);
  }

  createUser(name: string) {
    const newUser = { id: Date.now(), name };
    this.users.push(newUser);
    return newUser;
  }
}
```

### Usage in a Module

```typescript
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  providers: [UserService],
  controllers: [UserController],
  exports: [UserService],
})
export class UserModule {}
```

### Injecting into a Controller

```typescript
import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';

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

  @Get()
  getAllUsers() {
    return this.userService.getUsers();
  }

  @Get(':id')
  getUser(@Param('id') id: number) {
    return this.userService.getUserById(id);
  }

  @Post()
  createUser(@Body('name') name: string) {
    return this.userService.createUser(name);
  }
}
```

### Value Providers

Value providers are useful when you need to provide a constant value. This is often used for configurations.

### Example: Configuration

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

const CONFIG = {
  database: 'my_database',
  apiKey: '123456789',
};

@Module({
  providers: [
    {
      provide: 'CONFIG',
      useValue: CONFIG,
    },
  ],
  exports: ['CONFIG'],
})
export class ConfigModule {}
```

Here, `CONFIG` is provided as a constant value that can be injected wherever needed.

### Usage in a Service

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

@Injectable()
export class AppService {
  constructor(@Inject('CONFIG') private config: any) {}

  getDatabaseName(): string {
    return this.config.database;
  }
}
```

### Factory Providers

Factory providers allow you to create a value dynamically using a factory function. This is useful when the value needs to be computed or when dependencies are required to create the value.

### Example: Database Connection

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

@Module({
  providers: [
    {
      provide: 'DATABASE_CONNECTION',
      useFactory: async () => {
        const connection = await createDatabaseConnection();
        return connection;
      },
    },
  ],
  exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}

async function createDatabaseConnection() {
  // Simulating database connection creation
  return {
    connect: () => console.log('Connected to database'),
    disconnect: () => console.log('Disconnected from database'),
  };
}
```

In this example, the `DATABASE_CONNECTION` is created dynamically using a factory function.

### Usage in a Service

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

@Injectable()
export class AppService {
  constructor(@Inject('DATABASE_CONNECTION') private dbConnection: any) {}

  connectToDatabase() {
    this.dbConnection.connect();
  }

  disconnectFromDatabase() {
    this.dbConnection.disconnect();
  }
}
```

### Existing Providers

Existing providers allow you to alias an existing provider. This is useful when you want to provide the same instance under different tokens.

### Example: Alias for Logging Service

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

@Injectable()
export class LoggingService {
  log(message: string) {
    console.log(message);
  }
}

@Module({
  providers: [
    LoggingService,
    {
      provide: 'Logger',
      useExisting: LoggingService,
    },
  ],
  exports: ['Logger'],
})
export class LoggingModule {}
```

### Usage in a Service

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

@Injectable()
export class AppService {
  constructor(@Inject('Logger') private logger: any) {}

  performAction() {
    this.logger.log('Action performed');
  }
}
```

## Using Providers in Modules

To use a provider, you need to register it in a module. Modules in NestJS act as organizational units that group related providers, controllers, and other modules together.

### Example: App Module

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

@Injectable()
export class LoggingService {
  log(message: string) {
    console.log(message);
  }
}

@Module({
  providers: [
    LoggingService,
    {
      provide: 'Logger',
      useExisting: LoggingService,
    },
  ],
  exports: ['Logger'],
})
export class LoggingModule {}
```

### Usage in a Service

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

@Injectable()
export class AppService {
  constructor(@Inject('Logger') private logger: any) {}

  performAction() {
    this.logger.log('Action performed');
  }
}
```

## Putting all things together

Combining these examples, let's see how they can be structured within modules and used together.

### App Module

```typescript
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { ConfigModule } from './config/config.module';
import { DatabaseModule } from './database/database.module';
import { LoggingModule } from './logging/logging.module';
import { AppService } from './app.service';
import { AppController } from './app.controller';

@Module({
  imports: [UserModule, ConfigModule, DatabaseModule, LoggingModule],
  providers: [AppService],
  controllers: [AppController],
})
export class AppModule {}
```

### App Service

```typescript
import { Injectable, Inject } from '@nestjs/common';
import { UserService } from './user/user.service';

@Injectable()
export class AppService {
  constructor(
    private readonly userService: UserService,
    @Inject('CONFIG') private config: any,
    @Inject('DATABASE_CONNECTION') private dbConnection: any,
    @Inject('Logger') private logger: any,
  ) {}

  performTask() {
    this.logger.log('Performing task...');
    const users = this.userService.getUsers();
    this.dbConnection.connect();
    console.log(`Database: ${this.config.database}`);
    this.dbConnection.disconnect();
    return users;
  }
}
```

### App Controller

```typescript
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getData() {
    return this.appService.performTask();
  }
}
```

By using these various types of providers and organizing them into modules, you can create a highly modular and maintainable NestJS application. Each type of provider serves a specific purpose, allowing you to manage dependencies effectively and inject them wherever needed in your application.

<details data-node-type="hn-details-summary"><summary>Conclusion</summary><div data-type="detailsContent">Providers are the backbone of NestJS's powerful dependency injection system. By understanding the different types of providers and how to use them effectively, you can create scalable, maintainable, and testable applications. Whether you're building services, repositories, or dynamic configurations, NestJS providers give you the flexibility and control you need to manage your application's dependencies seamlessly.</div></details>

Happy coding with NestJS!
