Understanding the Importance of @Injectable in NestJS
Tags: nestjs, javascript
Published: 2024-05-10 10:36:38

When we dive into the world of modern web development, especially with frameworks like NestJS, we encounter a lot of concepts that are both powerful and a bit complex to grasp at first. One of these concepts is Dependency Injection (DI), a design pattern used extensively in building scalable and easily maintainable applications. In NestJS, this pattern is beautifully simplified through the use of decorators like `@Injectable`. Let's explore how this little piece of code makes our development life easier.

#### The Basics: What is `@Injectable`?

In simple terms, when you annotate a class with `@Injectable()`, you are essentially [storing metadata using the reflect-metadata library](https://github.com/nestjs/nest/blob/master/packages/common/decorators/core/injectable.decorator.ts#L43-L48), then when you add the class to the providers array you're telling NestJS: "Hey, please manage this class for me!" What does that mean? It means that NestJS will take care of creating an instance of this class and manage its entire lifecycle, based on the scope you define—singleton, request, or transient. But there’s more to it.

Consider a simple service in a NestJS app, like `CarService`:

```typescript
export class CarService {
  getMake(): string {
    return 'Tesla';
  }
}
```

Without following NestJS's conventions, you might end up manually creating an instance of `CarService` inside your controllers or other services. However, this approach is not just cumbersome but also contrary to the principles of good software architecture that NestJS advocates.

#### The Right Way: Leveraging DI

Instead of manually handling the object lifecycle, you should lean on NestJS's powerful DI system. By marking `CarService` as `@Injectable` and registering it in a module, NestJS handles its instantiation and ensures that it's ready to be injected wherever it's needed:

```typescript
@Injectable()
export class CarService {
  getMake(): string {
    return 'Tesla';
  }
}

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, CarService /* register as a provider */],
})
export class AppModule {}
```

Now, `CarService` can be easily injected into any controller or service without the need to manually create an instance:

```typescript
@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly carService: CarService,
  ) {}

  @Get()
  getHello(): string {
    console.log(this.carService.getMake());
    return this.appService.getHello();
  }
}
```

#### What If You Forget `@Injectable`?

Interestingly, even if you don’t explicitly use `@Injectable()` in your classes, NestJS's DI mechanism might still handle the injection correctly if the class doesn’t depend on other providers. However, this works until it doesn't—especially when your classes start having dependencies of their own.

For instance, if `CarService` requires `LoggerService`, you must decorate `CarService` with `@Injectable()` to ensure that `LoggerService` is properly injected:

```typescript
@Injectable()
export class LoggerService {
  log(message: string): void {
    console.log(message);
  }
}

export class CarService {
  constructor(private readonly loggerService: LoggerService) {}

  getMake(): string {
    console.log(this.loggerService); // 💣💥
    return 'Tesla';
  }
}
```

_If CarService is not properly decorated with `@Injectable`, `this.loggerService` would be `undefined`._

#### Conclusion: Embrace the Simplicity

NestJS's DI system is robust and designed to simplify your development process. By using `@Injectable()`, you ensure that your services are properly managed and that dependencies are handled correctly. This not only makes your code cleaner and more modular but also aligns with best practices that allow for greater scalability and easier maintenance.

So, next time you’re setting up services in your NestJS application, remember the importance of `@Injectable()`. It’s a small annotation that carries a lot of weight, making your coding journey much smoother and enjoyable. Happy coding!