Detailed Explanation of NestJS Services: Creating, Injecting Services, and Managing Business Logic

Services are one of the core concepts in NestJS, used to manage the business logic of an application. This article will provide a detailed introduction to the concept of services in NestJS, explaining how to create and inject services, and how to manage business logic through services.

Overview of Services

Services are classes used to encapsulate and manage business logic. In NestJS, services are typically used through the dependency injection mechanism. Services can be injected into controllers, other services, or modules to provide reusable and decoupled business logic.

Creating a Service

In NestJS, creating a service is very straightforward. We can use the NestJS CLI to generate a service, or create a service manually. Here is an example of creating a service using the NestJS CLI:

Terminal window
1
nest generate service users

The command above will generate a UsersService file, and we add the following content:

1
import { Injectable } from "@nestjs/common";
2
3
@Injectable()
4
export class UsersService {
5
private readonly users = [];
6
7
findAll() {
8
return this.users;
9
}
10
11
findOne(id: string) {
12
return this.users.find((user) => user.id === id);
13
}
14
15
create(user) {
16
this.users.push(user);
17
}
18
19
update(id: string, user) {
20
const existingUser = this.findOne(id);
21
if (existingUser) {
22
Object.assign(existingUser, user);
23
}
24
}
25
26
remove(id: string) {
27
const index = this.users.findIndex((user) => user.id === id);
28
if (index !== -1) {
29
this.users.splice(index, 1);
30
}
31
}
32
}

In the example above, UsersService is a class with the @Injectable() decorator. This decorator tells NestJS that this class is an injectable service.

Injecting Services

In NestJS, services can be injected into controllers or other services via dependency injection. Here is an example of injecting UsersService into UsersController:

1
import {
2
Controller,
3
Get,
4
Post,
5
Put,
6
Delete,
7
Param,
8
Body,
9
} from "@nestjs/common";
10
import { UsersService } from "./users.service";
11
12
@Controller("users")
13
export class UsersController {
14
constructor(private readonly usersService: UsersService) {}
15
16
@Get()
17
findAll() {
18
return this.usersService.findAll();
19
}
20
21
@Get(":id")
22
findOne(@Param("id") id: string) {
23
return this.usersService.findOne(id);
24
}
25
26
@Post()
27
create(@Body() createUserDto: CreateUserDto) {
28
return this.usersService.create(createUserDto);
29
}
30
31
@Put(":id")
32
update(@Param("id") id: string, @Body() updateUserDto: UpdateUserDto) {
33
return this.usersService.update(id, updateUserDto);
34
}
35
36
@Delete(":id")
37
remove(@Param("id") id: string) {
38
return this.usersService.remove(id);
39
}
40
}

In the example above, we inject UsersService into the constructor of UsersController. This way, we can use the business logic provided by the service in the methods of the controller.

Managing Business Logic

Services are used to encapsulate and manage business logic, separating it from controllers, making the code more modular and maintainable. Here are some common examples of managing business logic:

Handling Data Storage

Services can be used to handle data storage and retrieval operations, such as querying or saving data from a database. Here is an example of handling user data storage:

1
import { Injectable } from "@nestjs/common";
2
import { User } from "./user.entity";
3
4
@Injectable()
5
export class UsersService {
6
private readonly users: User[] = [];
7
8
findAll(): User[] {
9
return this.users;
10
}
11
12
findOne(id: string): User {
13
return this.users.find((user) => user.id === id);
14
}
15
16
create(user: User) {
17
this.users.push(user);
18
}
19
20
update(id: string, user: User) {
21
const existingUser = this.findOne(id);
22
if (existingUser) {
23
Object.assign(existingUser, user);
24
}
25
}
26
27
remove(id: string) {
28
const index = this.users.findIndex((user) => user.id === id);
29
if (index !== -1) {
30
this.users.splice(index, 1);
31
}
32
}
33
}

Handling Business Rules

Services can be used to handle complex business rules and logic, such as calculating discounts or validating user input. Here is an example of handling user validation:

1
import { Injectable } from "@nestjs/common";
2
3
@Injectable()
4
export class AuthService {
5
validateUser(username: string, password: string): boolean {
6
// Assume there is a list of users
7
const users = [{ username: "test", password: "password" }];
8
9
const user = users.find(
10
(user) => user.username === username && user.password === password,
11
);
12
return !!user;
13
}
14
}

Calling External APIs

Services can also be used to call external APIs, such as fetching data via HTTP requests or interacting with third-party services. Here is an example of calling an external API:

1
import { Injectable, HttpService } from "@nestjs/common";
2
import { map } from "rxjs/operators";
3
4
@Injectable()
5
export class ExternalApiService {
6
constructor(private readonly httpService: HttpService) {}
7
8
fetchData() {
9
return this.httpService
10
.get("https://api.example.com/data")
11
.pipe(map((response) => response.data));
12
}
13
}

Conclusion

This article provided a detailed introduction to the concept of services in NestJS, explaining how to create and inject services, and how to manage business logic through services. Services play a crucial role in NestJS, encapsulating business logic to make the code more modular and maintainable.