Node.js Framework Series — 1.2.3. NestJS — Providers/Services

In NestJS, providers have a vital importance. In general we can use the following classes as a provider:

  • Services: contains domain (business) logic.
  • Repositories: contains database CRUD operations
  • Factories
  • Helpers

and so on… objects can be used as a provider in NestJS application. The main key features of providers are that they can inject dependencies. It means that we can inject them to other classes, wherever we want easily. NestJS will manage the dependencies. As we said before, NestJS is inspired by Angular.js, therefore NestJS supports IoC(Inversion of Control) and DI(Dependency Injection). It manages providers lifecycle. To convert one class to a provider, simply annotate your class with @Injectable() decorator as shown below:

notification.service.ts

import { Injectable } from '@nestjs/common';@Injectable()
export class NotificationService {}

as you see above, @Injectable() decorator transforms the class to the provider. Thus, we can inject this notification service to our controller, other to any other object. In general, we use provider as a service from controllers. Controller handles the HTTP requests and forwards to service classes for the business processes. But NestJS providers can do more than that.

We will deep dive to providers the following topics:

  • Services
  • DI(Dependency Injection)
  • Scopes
  • Custom providers
  • Optional providers
  • Property-based injection
  • Provider registration

Services

Let’s continue to examples with our property user microservice. We created several component/module in it. Let’s create service class for the notification module and inject it to the controller and register it to module together. As shown below, we can create our service as follows:

NestJS — Provider/Service diagram

PS, to create a service using the NestJS CLI, head back to terminal and run the following commands:

$ nest g s notification

notification.service.ts

import { Injectable } from '@nestjs/common';
import { NotificationPool } from './interface/notification-pool.interface';
@Injectable()
export class NotificationService {
private readonly notifications: NotificationPool[] = [];
create(data: NotificationPool) {
this.notifications.push(data);
}
findAll(): NotificationPool[] {
return this.notifications;
}
}

As you see above, notification service has one property and two methods. We used the @Injectable() decorator for to make it provider. That’s all.

We defined an interface for the service methods which is best practice for every programming language as shown below:

notification-pool.interface.ts

export interface NotificationPool {
userId: number;
notificationType: string;
messageFrom: string;
message: string;
}

Our service class is ready for the usage now. Let’s inject it to the controller first as shown below:

notification.controller.ts

import { CreateNotificationPoolDto } from './dto/notification-pool.dto';
import { NotificationService } from './notification.service';
import { Body, Controller, Get, Post } from '@nestjs/common';
import { NotificationPool } from './interface/notification-pool.interface';
@Controller('notification')
export class NotificationController {
constructor(private notificationService: NotificationService) {}
@Post()
async create(@Body() createNotificationPoolDto: CreateNotificationPoolDto) {
this.notificationService.create(createNotificationPoolDto);
}
@Get()
async findAll(): Promise<NotificationPool[]> {
return this.notificationService.findAll();
}
}

As you see above, we inject our service to controller in a constructor. That’s all. As you see on the code, we used DTO (Data Transfer Object), let’s create it as shown below:

notification-pool.dto.ts

import { NotificationPool } from './../interface/notification-pool.interface';
export class CreateNotificationPoolDto implements NotificationPool {
userId: number;
notificationType: string;
messageFrom: string;
message: string;
}

One more thing left, let’s register our service class to module class as shown below (If you create a service by using NestJS CLI tool, it will automatically register a service to appropriate module):

notification.module.ts

import { Module } from '@nestjs/common';
import { NotificationController } from './notification.controller';
import { NotificationService } from './notification.service';
@Module({
controllers: [NotificationController],
providers: [NotificationService],
exports: [NotificationService],
})
export class NotificationModule {}

Finally, folder tree as follows:

.├── dto│   └── notification-pool.dto.ts├── interface│   └── notification-pool.interface.ts├── notification.controller.spec.ts├── notification.controller.ts├── notification.module.ts├── notification.service.spec.ts└── notification.service.ts

Let’s test our code with Postman as follows:

$ npm run start

Go to Postman and create a HTTP POST reguest as follows:

Notification Endpoints test with Postman — HTTP Post method test

Sample JSON data:

{
"userId": 1,
"notificationType": "NOTIFICATION",
"messageFrom": "ADMIN",
"message": "Your account is activated"
}

You can test it with CURL, head back to terminal and run below commands to test:

$ curl --location --request POST 'localhost:3000/notification' \
--header 'Content-Type: application/json' \
--data-raw ' {
"userId": 1,
"notificationType": "NOTIFICATION",
"messageFrom": "ADMIN",
"message": "Your account is activated"
}'

Let’s retrieve notification data on Postman as follows:

Notification Endpoints test with Postman — HTTP Get method test

CURL command as follows for the test:

$ curl --location --request GET 'localhost:3000/notification'

Dependency injection

DI is one of the most important aspects of NestJS. By providing the support, NestJS allows us to write loosely coupled code, which, in turn, is also easily testable. Thanks to Nest being written in TypeScript, the dependencies are resolved just by type — there’s no need to inject them by hand!

For example, in our previous notification component/module example, we injected our service class to controller as follows:

constructor(private notificationService: NotificationService) {}

As you see above, this dependency is resolved and passed to our controller’s constructor.

PS, NestJS is inspired by AngularJS, we recommend reading an article about DI in the official Angular[1] documentation as well.

Scopes

He had talked about the scope on the controller section. Providers have a lifetime (“scope”) synchronised with the application lifecycle. When the application bootstrapped all of the dependencies should be resolved, and when application shuts down, all of the providers will be destroyed. As we talk before, NestJS provides three types of scopes as DEFAULT (Singleton scope), REQUEST and TRANSIENT.

Custom providers

NestJS has a built in IoC(Inversion of Control) container that resolves relationship between providers. It manages DI(Dependency Injection) mechanism as we describe above. To define a provider we use @Injectable() decorator, but we have more than that. We can use plain values, classes, asynchronous or synchronous factories as well.

Optional providers

Sometimes we don’t need to resolve all of the dependencies. They can be resolved, when they get the right request parameters. To indicate a provider is optional, we can use the @Optional() decorator in the constructor’s signature as follows:

constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T){}

Property-based injection

NestJS default injection type is a contructor-based injection. It mean that we injects provider via constructors. It is not always possible, for instance if ypur top-level class depends on one or more providers, passing them all by calling super() in sub-classes from the constructor can be complex or not possible. Instead of this, we can use property-based injection method by using @Inject() decorator at the property level.

Property registration

All of the providers cannot be used by default in other object/class. To do so, we need to register them in a module class as shown below:

notification.module.ts

import { Module } from '@nestjs/common';
import { NotificationController } from './notification.controller';
import { NotificationService } from './notification.service';
@Module({
controllers: [NotificationController],
providers: [NotificationService],
})
export class NotificationModule {}

Resources:

[1] Dependency Injection , AngularJS Documentation, 2020, available at https://angular.io/guide/dependency-injection

--

--

Rasim Sen/Blockchain Architect-zero2hero founder

I am a blockchain architect, close on 2 decades of experience as a developer, I’ve got 5 products in blockchain, and I like to talk about tech,sci-fi, futurism!