Detailed Explanation of the Main Files and Directories in the src Directory of a NestJS Project

The directory structure of a NestJS project is clear and highly scalable. Understanding and properly using the roles of various files and directories in a project is crucial for building efficient and maintainable projects. This article will provide a detailed explanation of the main files and directories in the src directory of a NestJS project and demonstrate how multiple modules collaborate through practical examples.

app.module.ts

The app.module.ts file is the root module of a NestJS application. It defines the main structure and dependencies of the application. All modules, controllers, and services are registered and managed through the root module.

1
import { Module } from "@nestjs/common";
2
import { TypeOrmModule } from "@nestjs/typeorm";
3
import { ConfigModule } from "@nestjs/config";
4
import { UsersModule } from "./users/users.module";
5
import { ProductsModule } from "./products/products.module";
6
7
@Module({
8
imports: [
9
ConfigModule.forRoot(),
10
TypeOrmModule.forRoot(),
11
UsersModule,
12
ProductsModule,
13
],
14
controllers: [],
15
providers: [],
16
})
17
export class AppModule {}

In the example above, AppModule imports UsersModule and ProductsModule, making them part of the application.

main.ts

The main.ts file is the entry point of a NestJS application. It is responsible for bootstrapping the application and starting the NestJS HTTP server.

1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
4
async function bootstrap() {
5
const app = await NestFactory.create(AppModule);
6
await app.listen(3000);
7
}
8
bootstrap();

In the example above, the main.ts file uses NestFactory to create a NestJS application based on AppModule and listens on port 3000.

controllers

Controllers in NestJS are classes used to handle HTTP requests. Each controller defines a set of routes that correspond to different request paths and methods.

1
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
2
import { UsersService } from "./users.service";
3
import { CreateUserDto } from "./dto/create-user.dto";
4
5
@Controller("users")
6
export class UsersController {
7
constructor(private readonly usersService: UsersService) {}
8
9
@Get()
10
findAll() {
11
return this.usersService.findAll();
12
}
13
14
@Get(":id")
15
findOne(@Param("id") id: string) {
16
return this.usersService.findOne(+id);
17
}
18
19
@Post()
20
create(@Body() createUserDto: CreateUserDto) {
21
return this.usersService.create(createUserDto);
22
}
23
}

In the example above, UsersController defines three routes handling the /users path: GET /users, GET /users/:id, and POST /users.

services

Services in NestJS are classes used to handle business logic. Services are typically injected into controllers to process requests and return data.

1
import { Injectable } from "@nestjs/common";
2
import { User } from "./user.entity";
3
import { InjectRepository } from "@nestjs/typeorm";
4
import { Repository } from "typeorm";
5
import { CreateUserDto } from "./dto/create-user.dto";
6
7
@Injectable()
8
export class UsersService {
9
constructor(
10
@InjectRepository(User)
11
private usersRepository: Repository<User>,
12
) {}
13
14
findAll(): Promise<User[]> {
15
return this.usersRepository.find();
16
}
17
18
findOne(id: number): Promise<User> {
19
return this.usersRepository.findOneBy({ id });
20
}
21
22
create(createUserDto: CreateUserDto): Promise<User> {
23
const user = this.usersRepository.create(createUserDto);
24
return this.usersRepository.save(user);
25
}
26
}

In the example above, UsersService uses TypeORM to handle CRUD operations for the User entity and provides findAll, findOne, and create methods.

modules

Modules in NestJS are classes used to organize the application structure. Each module can contain multiple controllers and services and can import other modules.

1
import { Module } from "@nestjs/common";
2
import { TypeOrmModule } from "@nestjs/typeorm";
3
import { UsersController } from "./users.controller";
4
import { UsersService } from "./users.service";
5
import { User } from "./user.entity";
6
7
@Module({
8
imports: [TypeOrmModule.forFeature([User])],
9
controllers: [UsersController],
10
providers: [UsersService],
11
})
12
export class UsersModule {}

In the example above, UsersModule imports TypeOrmModule and registers the User entity, while also defining UsersController and UsersService.

Collaboration Between Multiple Modules

In practical projects, multiple modules need to collaborate. Below, we use UsersModule and ProductsModule as an example to demonstrate how to use a service from one module in another module.

First, create a ProductsModule:

Terminal window
1
nest generate module products
2
nest generate controller products
3
nest generate service products

Define the Product entity:

1
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
2
3
@Entity()
4
export class Product {
5
@PrimaryGeneratedColumn()
6
id: number;
7
8
@Column()
9
name: string;
10
11
@Column()
12
price: number;
13
}

Configure ProductsModule:

1
import { Module } from "@nestjs/common";
2
import { TypeOrmModule } from "@nestjs/typeorm";
3
import { ProductsController } from "./products.controller";
4
import { ProductsService } from "./products.service";
5
import { Product } from "./product.entity";
6
import { UsersModule } from "../users/users.module"; // Import UsersModule
7
8
@Module({
9
imports: [TypeOrmModule.forFeature([Product]), UsersModule], // Import UsersModule
10
controllers: [ProductsController],
11
providers: [ProductsService],
12
})
13
export class ProductsModule {}

Use UsersService in ProductsService:

1
import { Injectable } from "@nestjs/common";
2
import { InjectRepository } from "@nestjs/typeorm";
3
import { Repository } from "typeorm";
4
import { Product } from "./product.entity";
5
import { UsersService } from "../users/users.service"; // Import UsersService
6
7
@Injectable()
8
export class ProductsService {
9
constructor(
10
@InjectRepository(Product)
11
private productsRepository: Repository<Product>,
12
private usersService: UsersService, // Inject UsersService
13
) {}
14
15
findAll(): Promise<Product[]> {
16
return this.productsRepository.find();
17
}
18
19
findOne(id: number): Promise<Product> {
20
return this.productsRepository.findOneBy({ id });
21
}
22
23
async create(name: string, price: number): Promise<Product> {
24
const user = await this.usersService.findOne(1); // Use UsersService
25
if (user) {
26
const product = this.productsRepository.create({ name, price });
27
return this.productsRepository.save(product);
28
}
29
return null;
30
}
31
}

In the example above, ProductsService injects UsersService through the constructor and uses its methods in the create method.

Conclusion

This article provides a detailed explanation of the main files and directories in the src directory of a NestJS project, including app.module.ts, main.ts, controllers, services, and modules, and demonstrates how multiple modules collaborate through practical examples. By organizing and using these files and directories properly, you can build efficient and maintainable NestJS projects.