Nestjs 是一个构建在Node.js Express服务器之上的现代Web框架。利用现代ES6 JavaScript提供的强大灵活性和TypeScript在编译期间强制实现类型安全,将可扩展的Node.js服务器提升到一个全新的水平。Nest将三种不同的技术结合为一个成功的组合,使应用程序高度可测试,可扩展,松散耦合和可维护:
- 面向对象编程(OOP):围绕对象而不是行为和可重用性构建的模型。
- 函数编程(FP):设计的函数不依赖于全局状态,即对于一些不改变的设置参数,函数f(x)每次都返回相同的结果。
- 函数式响应型编程(FRP):扩展上面的FP和响应型编程。函数式响应型编程是其核心功能编程,它考虑了跨时间的流程。它在UI,模拟,机器人和其他应用程序中非常有用,其中特定时间段的结果可能与另一个时间段的结果不同。
主题讨论
1. Nest CLI
Nest的第5版中新增了一个CLI命令,允许通过命令行生成项目和文件。 全局安装CLI:
npm install -g @nestjs/cli
或者通过Docker:
docker pull nestjs/cli:[version]
可以使用以下命令生成新的Nest项目:
nest new [project-name]
此过程将询问 name, description, version (defaults to 0.0.0), and author (你的名字)。 完成此过程后,您将拥有一个设置好的Nest项目,其中依赖项安装在 node_modules 文件夹。 new 命令还会询问你想用什么包管理器,以同样的方式,有 yarn 或 npm 可以用。
CLI中最常用的命令是 generate (g) 命令, 允许你创建新的 controllers, modules, servies 或Nest支持的任何其它组件:
class(cl)controller(co)decorator(d)exception(e)filter(f)gateway(ga)guard(gu)interceptor(i)middleware(mi)module(mo)pipe(pi)provider(pr)service(s)
括号中的字符串是该特定命令的别名
nest generate service [service-name]
在控制台中,您可以输入:
nest g s [service-name]
最后,Nest CLI提供 info (i) 命令以显示项目的有关信息。这个命令将输出如下信息:
[System Information]
OS Version : macOS High Sierra
NodeJS Version : v8.9.0
YARN Version : 1.5.1
[Nest Information]
microservices version : 5.0.0
websockets version : 5.0.0
testing version : 5.0.0
common version : 5.0.0
core version : 5.0.0
2.依赖注入
依赖注入是一种技术,它提供一个依赖对象,如一个模块或者一个组件,然后依赖的对象就像服务一样,可以把它注入到组件的构造函数中。 举个例子,在这里我们将 UserRespository 服务注入到 UserService 的构造函数中, 从而提供从 UserService 组件内部访问User Database存储库的权限。
@Injectable()
export class UserService implements IUserService {
constructor(@Inject('UserRepository') private readonly UserRepository: typeof User) { }
...
}
然后,这个 UsersService 将被注入到 UsersController,将提供控制器的路由对 UsersService 访问。后续章节中会介绍路由和依赖注入的更多信息。
3.认证
身份验证是开发过程中最重要的方面之一。 作为开发人员,我们始终希望确保用户只能访问他们有权访问的资源。 近年来,身份验证方法已经扩展到变得更加复杂,但我们仍然需要相同的服务器端逻辑,以确保这些经过身份验证的用户始终是合法用户,并将这种身份验证持久化,这样就不需要为每一个 REST API 或 Websocket 的调用者重新进行身份验证,因为那将提供非常糟糕的用户体验。
Nest 选择集成Node.js生态系统中非常有名的认证库 Passport,同时使用JWT(JSON Web Token)策略。 Passport是在调用控制器上的端点之前传递HTTP调用的中间件。下面是为示例项目编写的 AuthenticationMiddleware ,它扩展了 NestMiddleware,根据请求有效负载中的电子邮件对每个用户进行身份验证。
@Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
constructor(private userService: UserService) { }
async resolve(strategy: string): Promise<ExpressMiddleware> {
return async (req, res, next) => {
return passport.authenticate(strategy, async (/*...*/args: any[]) => {
const [, payload, err] = args;
if (err) {
return res.status(HttpStatus.BAD_REQUEST).send('Unable to authenticate the user.');
}
const user = await this.userService.findOne({
where: { email: payload.email }
});
req.user = user;
return next();
})(req, res, next);
};
}
}
Nest还实现了Guards,与其他提供程序一样,使用@Injectable()装饰器进行装饰。 Guards 可以控制用户可以访问的内容端点。 将在后续“身份验证”章节中更详细地讨论 Guards。
4.ORM
ORM是服务器和数据库之间通信时最重要的概念之一:对象关系映射。 ORM提供内存中对象(诸如定义的 User 或 Comment 类) 和数据库中的关系表之间的映射。 允许您创建一个数据传输对象,该对象知道如何将存储在内存中的对象写入数据库,并将结果从SQL或其他查询语言读回内存。在这,我们将讨论三种不同的ORM: 两种关系型 orm 和一种针对 NoSQL 数据库的 orm。
TypeORM是Node.js中最成熟和最流行的ORM之一, Nest为其提供自己的包: @nestjs/typeorm,TypeORM非常强大,并且支持许多数据库,如MySQL,PostgreSQL,MariaDB,SQLite,MS SQL Server,Oracle和WebSQL。同时,Sequelize也是关系数据库的另一个ORM。
如果TypeORM是最流行的ORM之一,那么Sequelize则是Node.js世界中最受欢迎的ORM之一。 它是用纯JavaScript编写的, 但是通过 sequelize-typescript 和 @types/sequelize 包进行了 TypeScript 绑定。Sequelize拥有强大的事务支持,关系,读取复制和更多功能。
最后一个ORM是处理非关系数据库或NoSQL数据库的ORM。mongoose 包处理MongoDB和JavaScript之间的对象关系。 与关系数据库相比,两者之间的实际映射关系要密切得多,因为MongoDB以JSON格式存储其数据,JSON格式代表JavaScript Object Notation。 Mongoose也有 @nestjs/mongoose ,并提供通过查询链查询数据库的能力。
5.REST API
REST是创建API的主要设计范例之一。它代表代表状态转移传输,并使用JSON作为传输格式,这与Nest存储对象的方式一致,因此它适合使用和返回 HTTP 调用。
客户端对服务器进行HTTP请求,服务器将根据URL和HTTP动词将调用路由到正确的Controller,可选择在到达Controller之前将其传递给一个或多个中间件。 然后,Controller将其交给service进行处理,其中可能包括通过ORM与数据库进行通信。
如果客户端请求资源(GET请求),则返回一个可选主体; 如果是 POST/PUT/DELETE,则返回一个200 / 201 HTTP OK,如果没有响应主体。
6.WebSockets
Websocket 是从服务器连接和发送 / 接收数据的另一种方式。
使用 websocket,客户端连接到服务器,然后订阅特定的通道。 然后,客户端可以将数据推送到订阅的通道。 服务器将接收此数据,然后将其广播到订阅该特定通道的每个客户端。 这使得多个客户端都可以接收实时更新,而无需手动进行 API 调用。 大多数聊天应用程序使用WebSockets进行实时通信,一旦其中一个成员发送消息,组消息中的每个人都将收到消息。 与传统的请求-响应 API 相比,Websockets 允许更多的流式数据传输方法,因为 Websockets 可以在接收到数据时广播数据。
7.微服务
微服务允许将Nest将应用程序结构化为松散耦合服务的集合。 在Nest中,微服务略有不同,因为它们是一个使用不同于 HTTP 的传输层的应用程序。该层可以是TCP或Redis pub / sub等。
Nest支持TCP和Redis,但如果您与另一个传输层结合,则可以使用 CustomTransportStrategy 接口实现它。 微服务很棒,因为它们允许团队在全局项目中处理自己的服务,并且在不影响项目其余部分的情况下对服务进行更改,因为它是松散耦合的。 这使得持续交付和持续集成独立于其他团队的微服务。
8.GraphQL
正如我们在上面看到的,REST 是设计 api 时的一种范例, 但是有一种新的方式来考虑创建和使用 api:GraphQL。
使用GraphQL,URL将接受带有JSON对象的查询参数,而不是每个资源都有自己的URL指向。 此JSON对象定义要返回的数据的类型和格式。 Nest通过 @nestjs/graphql 包提供了这方面的功能。
这将包括项目中GraphQLModule, 它是Apollo服务器的包装器。
9.路由
在讨论Web框架时,路由是核心之一。当客户端需要访问服务器的端点时, 这些端点中的每一个都描述了如何检索/创建/操作存储在服务器上的数据。 描述API端点的每个Component必须具有@Controller() 装饰器,用于描述该组件的端点集的 API 前缀。
@Controller('hello')
export class HelloWorldController {
@Get(‘world’)
printHelloWorld() {
return ‘Hello World’;
}
}
10.Nestg工具装饰器
@Module:项目中此可重用代码包的定义,它接受以下参数来定义其行为。
⋅⋅Imports:包含此模块中使用的组件。
⋅⋅Exports:导出模块。
⋅⋅Components:通过 Nest 注入的组件在这个模块上共享。
⋅⋅Controllers:在此模块中创建的控制器,这些控制器将根据定义的路由定义API端点。
@Injectable:Nest中几乎所有东西都是可以通过构造函数注入的提供者。提供者用@Injectable()装饰。
..Middleware:在将请求传递给路由处理程序之前运行的函数。
..Interceptor:类似于中间件,它们在执行方法之前和之后绑定额外的逻辑,它们可以转换或完全覆盖函数。拦截器的灵感来自面向方面编程(AOP)。
..Pipe:与拦截器功能的一部分类似,管道将输入数据转换为所需的输出。
..Guard:一个更聪明,更小众的中间件,Guards的唯一目的是确定路由器处理程序是否应该处理请求。
..* Catch:告诉ExceptionFilter要查找的异常,然后将数据绑定到它。
@Catch:将元数据绑定到异常过滤器,并告诉Nest过滤器只查找@Catch中列出的异常。
11.OpenAPI (Swagger)
在编写 Nest 服务器时,api文档是非常重要的,否则使用该 API 开发客户端的开发人员不知道发送什么或将得到什么。
最流行的文档引擎之一是 Swagger。 像其他模块一样,Nest 为 OpenAPI (Swagger) spec,提供了一个专用模块 @nestjs/swagger。
这个模块提供了装饰器来帮助描述 API 的输入 / 输出和端点。 然后可以通过服务器上的一个端点访问该文档。
12.命令查询责任分离(CQRS)
命令查询责任分离(Command Query Responsibility Segregation,CQRS)的思想是,每个方法要么是执行操作(Command)的方法,要么是执行请求数据(Query)的方法。
在我们的示例应用程序的上下文中,不会在控制器中直接为一个端点创建数据库访问代码,而是创建一个组件(数据库服务) ,该组件具有getAllUsers()这样的方法,它将返回控制器服务可以调用的所有用户,从而将问题和答案分离到不同的组件中。
13.测试
测试你的 Nest 服务器将是必要的,确保部署时没有不可预见的安全性问题。
有两种不同类型的测试: 单元测试和 E2E 测试(端到端测试)。 单元测试是测试小代码片段或代码块,它可以像测试单个函数或 Controller、 Interceptor 或任何其他Injectable测试一样精细。
现在有很多流行的单元测试框架,Jasmine 和 Jest 是两个流行的框架。 Nest 提供了特殊的包,@nestjs/testing,用于在 *.spec.ts 及 *.test.ts 类中编写单元测试。
E2E 测试是另一种常用的测试形式,它不同于单元测试,它测试整个功能,而不是测试单个功能或组件,而这正是端到端测试这个名称的来源。
当应用程序变得非常庞大,以至于很难绝对地测试每一段代码和端点。 在这种情况下,您可以使用 E2E 测试从头到尾测试应用程序,以确保一切顺利进行。
对于 E2E 测试,Nest 应用程序可以再次使用 Jest 库来模拟组件。 除了 Jest 之外,还可以使用 supertest 库来模拟 HTTP 请求。