Introduction to NestJS Services

January 8, 2020

Last updated: April 6, 2021

1,005 words

Post contents

Service

In enterprise applications, we follow the SOLID principle, where S stands for Single Responsibility.

The controllers are responsible for accepting HTTP requests from the client and providing a response. For providing the response, you may need to connect to some external source for data.

If we add the code to connect to the external source inside, we are not following the single responsibility principle.

To avoid this issue, you use services, which will be responsible for providing some data, which can be reused across the application. It can also hold some validation logic or logic to validate users.

Creating and Using the Service

There are two types of services that can be created in NestJS:

  • Class-based Provider
  • Non-class-based Provider

Note: If you are coming from Angular, there are high chances you already know these concepts.

Class-based Provider

To create a class-based provider, we can use the CLI command below, the command will create the service inside the product folder.

nest generate service product

In the product folder, you will find two files:

  • product.service.ts (For logic.)
  • product.service.spec.ts (For unit testing.)

You may end up using multiple services for a feature or even multiple types of providers.

Using a class-based Provider

Now open the product.service.ts file and add the below code, we will move some code from ProductController to ProductService.

import { Injectable } from '@nestjs/common';@Injectable()export class ProductService {    products = [        { id: 1, name: 'One Plus 7', price: 48000 },        { id: 2, name: 'I Phone X', price: 64999 }    ];    getProducts() {        return this.products;    }    addProduct(product:any){        this.products.push(product);    }    getProductById(id:number) {        return this.products.find(p => p.id === id);    }}

As the service is ready now, open product.controller.ts and make the below changes.

import { ProductService } from './product.service';@Controller('product')export class ProductController {    constructor(private productService: ProductService) {}    @Get()    GetProducts() {        return this.productService.getProducts();    }    @Post()    AddProduct(@Req() req: Request, @Res() res: Response) {        this.productService.addProduct(req.body);        // return json data with default status code        return res.json({ id: req.body.id });        // to update the status code        //return res.status(205).json({ id: req.body.id})    }    @Get(':id')    GetProductById(@Param() param: any) {        return this.productService.getProductById(+param.id);    }}

The way ProductService is used here is known as dependency injection.

Like Controllers, Services need to be registered as well, the CLI does this for us, you can do it manually by adding it to the providers array of the module.

providers: [AppService, ProductService]

There is more about class-based services which we will cover in upcoming articles.

Non-class-based Providers

We can also create a service that is not a class-based service. There are two types:

  • Tokens: We can use string value as the token.
  • Factory: Useful when we have a service that needs some data from another service.

Creating tokens

You can create an injection token to use as service, to do that, create a new file product.token.ts inside the product folder and add the below code:

export interface Product {    endPoint: string;}export const PRODUCT = 'PRODUCT';export const Product_Token : Product = {    endPoint: 'http://localhost:3000/product'}

Now open app.module.ts and register the token using the providers property.

import { PRODUCT, Product_Token } from './product/product.token';providers: [{    provide : PRODUCT,    useValue: Product_Token}]

Next, open the product.service.ts and let’s use this token and add the below code. This is just for demo purposes, in the real-time application we may want to use this value.

import { Injectable, Inject } from '@nestjs/common';import { PRODUCT, Product } from './product.token';constructor(@Inject(PRODUCT) product: Product) {    console.log(product.endPoint);}

Once you run the application using the value, endPoint will be logged on the console.

Using factory

Factories are another type of provider and are available for a very special use case.

Generally, when we provide a service, they are resolved when the modules are loaded, but there may be instances where we need to create the instance dynamically, this is where we need factories.

For example, getting the database connection, for a client at runtime deciding which database to connect to.

Run the below commands to create two services:

nest generate service dbprovidernest generate service client

Add the below code in client.service.ts.

import { Injectable } from '@nestjs/common';@Injectable()export class ClientService {    getClientDetails() {        return {            client: 'test',            db: 'databaseconnection'        }    }}

Next, open dbprovider.service.ts and add the below code.

import { Injectable } from '@nestjs/common';@Injectable()export class DbproviderService {    constructor(private connection: string) { }    getProductsForClient() {        return this.connection;    }}

In dbprovider.service.ts, here we are using a string property, if you try to run this application, you will get the error as this is not allowed.

We want to create the instance of DbproviderService at runtime, so we need to make one more change. Open app.module.ts and remove DbproviderService from the providers property.

NestJS lets us create the factory, create a new file connection.provider.ts, and add the below code.

import { ClientService } from "./client/client.service";import { DbproviderService } from "./dbprovider/dbprovider.service";export const dbConnectionFactory  = {    provide: 'ClientConnection',    useFactory : (clientSerice: ClientService) => {        return new DbproviderService(clientSerice.getClientDetails().db);    },    inject: [ClientService]}

Here we are creating a new instance of DbproviderService by getting db from ClientService. You can use multiple services here, you just need to pass them comma-separated in useFactory and the same services need to be added in the inject property.

Now we are done with the factory, let’s register and use it. Open app.module.ts and add dbConnectionFactory in the providers property.

Next, open product.service.ts and add the below code.

constructor(@Inject(PRODUCT) product: Product,    @Inject('ClientConnection') dbProviderService: DbproviderService){    console.log(product.endPoint);    console.log(dbProviderService.getProductsForClient())}

Conclusion

We learned about how to create and use different types of providers in NestJS, we used the dependency injection design pattern to use services, which lets you achieve single responsibility as well.

The services are singleton, but we can also control the scope of Services, which we will see in the next article.

Subscribe to our newsletter!

Subscribe to our newsletter to get updates on new content we create, events we have coming up, and more! We'll make sure not to spam you and provide good insights to the content we have.