图解Nestjs - 适合中国宝宝的入门指导
笔者入门Nest的时候属实是迷糊了一阵,本文将从初学者的视角出发,试图为大家解释Nestjs到底是如何运作的。如有错误欢迎指出,谢谢~
假设我们来做这样一个服务:宝可梦大全
提供四个接口:
- 获取完整的宝可梦列表
- 根据宝可梦编号获取某一只宝可梦的信息
- 获取完整的技能列表
- 根据某个技能获取可以学会该技能的宝可梦列表
Module = 模块
Module,中文译作模块,我们将从它来入手,搞清楚Nestjs大概是如何工作的。
官网这张图表达的很清楚,Nest的大致理念就是一颗“模块树”,从根模块出发,连接到许多的子功能模块。
我们的宝可梦查询的结构可能是这样的:
(本文中我们使用Prisma来操作数据库)
现在让我们聚焦到其中一个功能模块:Pokemon Module
细说Pokemon Module
这个模块的领域是宝可梦,显然这是一个非常核心的模块。那么“模块”,又是怎么运作的呢?
从外部看,“模块”是一个黑盒,有自己的输入和输出:
而黑盒的内部,则主要是两部分:
- Services
- Controllers
Controllers是接收请求的入口,Services则是方法实现,这个应该不难理解
那么我们仔细看看所谓的输入输出是什么。
Module的输入是什么?
由于宝可梦的信息是存在数据库中的,因此宝可梦模块需要Prisma模块来与数据库交互。所以Prisma Module是Pokemon Module的输入。
Module的输出是什么?
既然我们说Prisma Module是Pokemon Module的输入,那,Prisma Module一定是输出了什么,对吧?
没错,Prisma Module的输出就是Prisma Service。Pokemon Module可以使用Prisma Service中的各种方法来交互数据库。
Provider?
在刚刚的叙述中,我们还没有提及在Nest中非常有存在感的Provider
。
我们说过,“Controllers是接收请求的入口,Services则是方法实现”,那么,Controllers是如何调用Services的呢?答案就是,当Services成为Provider时。
这就像是同一样东西的两种名字。它既是Pokemon Service,也是Pokemon Module的Provider,取决于从什么视角去看他。因此,在代码中它往往表现为这样:
- 1
- 2
- 3
- 4
- 5
- 6
// nestjs project directory
|-pokemon
| |_pokemon.module.ts
| |_pokemon.controller.ts
| |_pokemon.service.ts
|-......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
// pokemon.module.ts
@Module({
controllers: [PokemonController],
providers: [PokemonService],
imports: [PrismaModule],
exports: [PokemonService],
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
// pokemon.controller.ts
export class PokemonController {
// 正是因为Module的providers中传入了PokemonService
// Controller的constructor才能接收到pokemonService参数
constructor(private readonly pokemonService: PokemonService) {}
@Get('/find/all')
async findAll() {
const allPokemons = await this.pokemonService.findAll();
// ......
}
@Get('/find/:id')
async findOne(
@Param('id', ParseIntPipe) id: number,
) {
const pokemon = await this.pokemonService.findOne(id);
// ......
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
// pokemon.service.ts
// 此行Injectable装饰器也是必要的
// 关于Dependency Injection的更多信息,建议参考Angular官方文档:https://angular.dev/guide/di
@Injectable()
export class PokemonService {
constructor(private prisma: PrismaService) {}
parsePokemon(pokemon) {
// ...
}
async findAll() {
const pokemons = await this.prisma.pokemon.findMany({
include: {
likes: true,
},
});
return pokemons.map((item) => this.parsePokemon(item));
}
async findOne(id: number) {
const pokemon = await this.prisma.pokemon.findUnique({
where: { id }
});
if (!pokemon) {
throw new NotFoundException(`pokemon ${id} not found`);
}
return this.parsePokemon(pokemon);
}
}
Provider vs Import
在上面的例子我们可以看到,由于Injectable的PokemonService被放入了module定义的providers
中,PokemonController便可以使用它,那么它的功能岂不是和imports
重叠了?
事实上,如果我们去掉imports
,将Prisma Service
也放到providers
中,我们的代码依然可以运行:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
// pokemon.module.ts
@Module({
controllers: [PokemonController],
providers: [PokemonService, PrismaService],
imports: [],
exports: [PokemonService],
})
直接先说结论:用Import。
具体来说,只要是用外部服务(也就是除去PokemonModule将PokemonService作为Provider这种情况),使用import更为合适。
区别在于:import module会复用已经创建的实例,而每个provider都会创建新的实例。
前者占用更少的内存,且一个可复用的实例在多处被使用在需求中也更加常见。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
// pokemon.module.ts
// 推荐
@Module({
controllers: [PokemonController],
providers: [PokemonService],
imports: [PrismaModule],
exports: [PokemonService],
})
// 不推荐
@Module({
controllers: [PokemonController],
providers: [PokemonService, PrismaService],
imports: [],
exports: [PokemonService],
})
总结
看一个Nestjs应用是怎么跑的,首先就是看明白Module之间是如何互相作用的。
那么在实现我们自己的Module时,只需要记住:
- 定义自己的controllers
- 定义自己的service
- 把自己的service放在providers中,这样controllers才能用service
- 如果需要用外部的service,将外部module放在imports里
- 如果自己的service也需要被其他module使用,将自己的service放到exports里
比如这样:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
// pokemon.module.ts
@Module({
controllers: [PokemonController],
providers: [PokemonService],
imports: [PrismaModule],
exports: [PokemonService],
})
// skill.module.ts
@Module({
controllers: [SkillController],
providers: [SkillService],
imports: [PrismaModule, PokemonModule],
})
好了,恭喜你,你已经完全掌握Nestjs了!
参考
原文链接
2024年10个最佳Node JS REST API框架
在这篇文章中,我们将探讨2023年排名前十的NodeJS API框架,了解它们的关键功能、性能、可扩展性、易用性、安全性和流行性,以及优缺点和使用它们的大公司和项目。无论您是从头开始构建新应用程序,还是对现有应用程序进行现代化改造,本指南都将帮助您选择使用哪种Node.js API框架。
网络系列之 HTTPS 工作原理,理解 HTTPS 加密、解密、验证及数据传输过程
HTTP 是一种 超文本传输协议(Hyper Text Transfer Protocol) HTTPS (Hyper Text Transfer Protocol over SecureSocket Layer)是在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性。