Creating and Managing Modules in NestJS: Dependencies and Communication Between Modules

Modules are the core building blocks of NestJS, helping us organize code and manage dependencies. This article will introduce how to create and manage modules in NestJS, explore dependencies and communication between modules, and provide example code to demonstrate module organization and dependency management in complex projects.

Creating and Organizing Modules

Creating a Module

In NestJS, modules are defined using the @Module() decorator. The following example shows how to create a simple UsersModule:

users/users.module.ts
1
import { Module } from "@nestjs/common";
2
import { UsersService } from "./users.service";
3
import { UsersController } from "./users.controller";
4
5
@Module({
6
providers: [UsersService],
7
controllers: [UsersController],
8
})
9
export class UsersModule {}

Organizing Modules

A typical NestJS project contains multiple modules, each responsible for different functionalities. For example, you can have an AuthModule for authentication, a ProductsModule for product management, and so on.

app.module.ts
1
import { Module } from "@nestjs/common";
2
import { UsersModule } from "./users/users.module";
3
import { AuthModule } from "./auth/auth.module";
4
import { ProductsModule } from "./products/products.module";
5
6
@Module({
7
imports: [UsersModule, AuthModule, ProductsModule],
8
})
9
export class AppModule {}

Dependencies Between Modules

Exporting and Importing Modules

Modules can export services using the exports property, allowing other modules to import and use these services. The following example shows how to export UsersService and import UsersModule in AuthModule:

users/users.module.ts
1
import { Module } from "@nestjs/common";
2
import { UsersService } from "./users.service";
3
import { UsersController } from "./users.controller";
4
5
@Module({
6
providers: [UsersService],
7
controllers: [UsersController],
8
exports: [UsersService], // Export UsersService
9
})
10
export class UsersModule {}
11
12
// auth/auth.module.ts
13
import { Module } from "@nestjs/common";
14
import { AuthService } from "./auth.service";
15
import { UsersModule } from "../users/users.module";
16
17
@Module({
18
imports: [UsersModule], // Import UsersModule
19
providers: [AuthService],
20
})
21
export class AuthModule {}

Communication Between Modules

Communication between modules is typically achieved through services. For example, AuthService can call UsersService to validate a user:

auth/auth.service.ts
1
import { Injectable } from "@nestjs/common";
2
import { UsersService } from "../users/users.service";
3
4
@Injectable()
5
export class AuthService {
6
constructor(private readonly usersService: UsersService) {}
7
8
async validateUser(username: string, pass: string): Promise<any> {
9
const user = await this.usersService.findOne(username);
10
if (user && user.password === pass) {
11
return user;
12
}
13
return null;
14
}
15
}

Module Organization and Dependency Management in Complex Projects

In complex projects, the organization of modules and dependency management become particularly important. Here are some best practices:

Using Feature Modules

Organize related functionality into feature modules. For example, place all user-related code in UsersModule and all product-related code in ProductsModule.

Using Shared Modules

Place commonly used services, pipes, guards, etc., in a shared module and import them where needed. For example, you can create a SharedModule to provide database connections, logging services, etc.

shared/shared.module.ts
1
import { Module } from "@nestjs/common";
2
import { DatabaseService } from "./database.service";
3
import { LoggerService } from "./logger.service";
4
5
@Module({
6
providers: [DatabaseService, LoggerService],
7
exports: [DatabaseService, LoggerService],
8
})
9
export class SharedModule {}
10
11
// app.module.ts
12
import { Module } from "@nestjs/common";
13
import { SharedModule } from "./shared/shared.module";
14
import { UsersModule } from "./users/users.module";
15
import { AuthModule } from "./auth/auth.module";
16
17
@Module({
18
imports: [SharedModule, UsersModule, AuthModule],
19
})
20
export class AppModule {}

Using Dependency Injection Between Modules

Ensure loose coupling between modules through dependency injection. For example, inject services via constructors rather than directly instantiating them within modules.

products/products.service.ts
1
import { Injectable } from "@nestjs/common";
2
import { DatabaseService } from "../shared/database.service";
3
4
@Injectable()
5
export class ProductsService {
6
constructor(private readonly databaseService: DatabaseService) {}
7
8
async findAll(): Promise<any[]> {
9
return this.databaseService.query("SELECT * FROM products");
10
}
11
}

Conclusion

This article introduced how to create and manage modules in NestJS, explored dependencies and communication between modules, and provided example code to demonstrate module organization and dependency management in complex projects.