本文为译文,原文地址:https://kamilmysliwiec.com/nest-final-release-is-here-node-js-framework-built-top-of-typescript,做者,@Kamil Myśliwiechtml
Nest 是一个强大的 Node.js Web 框架,能够帮助你轻松地构建高效,可扩展的应用程序。它采用现代 JavaScript,基于 TypeScript 构建,并结合了 OOP(面向对象编程)和 FP (函数式编程)的最佳概念。node
它不只是又一个框架。你没必要等待一个大型的社区,由于 Nest 创建在著名仓库 Express 和 socket.io 之上。这意味着,你能够快速开始使用框架,而没必要担忧第三方插件的缺失。git
Nest 的核心概念是提供一种架构,帮助开发者实现层的最大分离,而且增长了应用程序的抽象。es6
Git:github
$ git clone https://github.com/kamilmysliwiec/nest-typescript-starter.git projectname $ cd projectname $ npm install $ npm run start
NPM:web
$ npm i --save @nestjs/core @nestjs/common @nestjs/microservices @nestjs/websockets @nestjs/testing reflect-metadata rxjs
Nest 采用 ES6 和 ES7 (decorators
, async / await
)功能构建。这意味着,使用它的最简单的方法是 Babel
或 TypeScript
。typescript
在本文中,我将使用 TypeScript
(它不是必须的!),我推荐你们选择这种方式。示例文件 tsconfig.json
以下:数据库
{ "compilerOptions": { "module": "commonjs", "declaration": false, "noImplicitAny": false, "noLib": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "es6" }, "exclude": [ "node_modules" ] }
记住 emitDecoratorMetadata
和 experimentalDecorators
必须设置为 true
。express
让咱们从头开始咱们的应用程序。首先,咱们必须被咱们的应用程序建立入口模块(app.module.ts
):npm
import { Module } from '@nestjs/common'; @Module({}) export class ApplicationModule {}
此时,模块的元数据(metadata
)为空({}
),由于咱们只想运行咱们的应用程序,并无加载任何控件或组件。
第二步,建立文件 index.ts
,并使用 NestFactory
基于咱们的模块类来建立 Nest 应用程序实例。
import { NestFactory } from '@nestjs/core'; import { ApplicationModule } from './app.module'; const app = NestFactory.create(ApplicationModule); app.listen(3000, () => console.log('Application is listening on port 3000'));
就这样。
若是要彻底控制 express
实例的生命周期,你能够简单的传递已建立的对象做为 NestFactory.create()
方法的第二个参数,像这样:
import express from 'express'; import { NestFactory } from '@nestjs/core'; import { ApplicationModule } from './modules/app.module'; const instance = express(); const app = NestFactory.create(ApplicationModule, instance); app.listen(3000, () => console.log('Application is listening on port 3000'));
这意味着,你能够直接添加一些自定义配置(例如,设置一些插件,如 morgan
或 body-parser
)。
控制层(Controllers
)负责处理传入的 HTTP 请求。在 Nest 中,控制器是一个带有 @Controller()
装饰器的类。
在上一节中,咱们为应用程序设置了入口点。如今,让咱们来构建咱们的第一个文件路径 /users
:
import { Controller, Get, Post } from '@nestjs/common'; @Controller() export class UsersController { @Get('users') getAllUsers() {} @Get('users/:id') getUser() {} @Post('users') addUser() {} }
正如你猜测的,咱们刚刚建立了一个具备 3 种不一样路径的路由:
GET: users GET: users/:id POST: users
没有必要重复 users
的每一个路径了吧?
Nest 容许咱们将额外的元数据传递给 @Controller()
装饰器 - 路径,这是每一个路由的前缀。让咱们重写咱们的控制器:
@Controller('users') export class UsersController { @Get() getAllUsers(req, res, next) {} @Get('/:id') getUser(req, res, next) {} @Post() addUser(req, res, next) {} }
正如你看到的, Nest 控制器中的方法和 Express
中的简单路由具备相同的参数列表和行为。
若是你想了解更多关于 req
(请求),res
(响应)和 next
,你能够阅读简短的路由文档。在 Nest 中,它们是等价的。
可是有一个重要的区别。 Nest 提供了一组自定义的装饰器,你可使用它们来标记参数。
Nest | Express |
---|---|
@Request() |
req |
@Response() |
res |
@Next() |
next |
@Session() |
req.session |
@Param(param?: string) |
req.params[param] |
@Body(param?: string) |
req.body[param] |
@Query(param?: string) |
req.query[param] |
@Headers(param?: string) |
req.headers[param] |
你能够这样使用它们:
@Get('/:id') public async getUser(@Response() res, @Param('id') id) { const user = await this.usersService.getUser(id); res.status(HttpStatus.OK).json(user); }
记住在文件的开头导入装饰器。
import { Response, Param } from '@nestjs/common';
UsersController
可使用,可是咱们的模块还不知道。让咱们打开 ApplicationModule
并添加一些元数据。
import { Module } from '@nestjs/common'; import { UsersController } from "./users.controller"; @Module({ controllers: [ UsersController ] }) export class ApplicationModule {}
你能够看到,咱们只须要将 controller
插入 controllers
数组中,这就够了。
几乎全部的东西都是组件,Service
, Repository
, Provider
等等。而且他们能够经过构造函数注入控制器或另外一组件。
在上一节中, 咱们构建了一个简单的 controller
,UsersController
。这个 controller
能够访问咱们的数据(我知道这是一个假数据,但这并不重要)。这不是一个很好的解决方案。咱们的控制器只能处理 HTTP 请求,并将更复杂的任务委托给服务(services
),这就是为何咱们要建立 UsersService
组件。
实际上,UsersService
应该从持久层调用适当的方法,例如, UsersRepository
组件。咱们没有任何类型的数据库,因此咱们再次使用假数据。
import { Component } from '@nestjs/common'; import { HttpException } from '@nestjs/core'; @Component() export class UsersService { private users = [ { id: 1, name: "John Doe" }, { id: 2, name: "Alice Caeiro" }, { id: 3, name: "Who Knows" }, ]; getAllUsers() { return Promise.resolve(this.users); } getUser(id: number) { const user = this.users.find((user) => user.id === id); if (!user) { throw new HttpException("User not found", 404); } return Promise.resolve(user); } addUser(user) { this.users.push(user); return Promise.resolve(); } }
Nest 组件是一个简单的类,使用 @Component()
注释。
在 getUser()
方法中能够看到,咱们使用了 HttpException
。它是 Nest 内置的异常,拥有两个参数,错误消息和状态代码。建立域异常是一个很好的作法,它应该扩展 HttpException
(更多见错误处理章节)。
咱们的服务准备好了。让咱们在 UsersController
中使用它。
@Controller('users') export class UsersController { constructor(private usersService: UsersService) {} @Get() getAllUsers(@Response req) { this.usersService.getAllUsers() .then((users) => res.status(HttpStatus.OK).json(users)); } @Get('/:id') getUser(@Response() res, @Param('id') id) { this.usersService.getUser(+id) .then((user) => res.status(HttpStatus.OK).json(user)); } @Post() addUser(@Response() res, @Body('user') user) { this.usersService.addUser(req.body.user) .then((msg) => res.status(HttpStatus.CREATED).json(msg)); } }
如图所示,UsersService
将被注入到构造函数中。
使用 TypeScript
来管理依赖关系很是简单,由于 Nest 会根据类型识别你的依赖关系。像这样:
constructor(private usersService: UsersService)
这就是你要作的所有。还有一个重要的事是,你必须在 tsconfig.json
中将 emitDecoratorMetadata
选项设置为 true
。
若是你不是 TypeScript
爱好者,而且使用纯 JavaScript
,则必须按如下方式执行:
import { Dependencies, Controller, Get, Post, Response, Param, Body, HttpStatus } from '@nestjs/common'; @Controller('users') @Dependencies(UsersService) export class UsersController { constructor(usersService) { this.usersService = usersService; } @Get() getAllUsers(@Response() res) { this.usersService.getAllUsers() .then((users) => res.status(HttpStatus.OK).json(users)); } @Get('/:id') getUser(@Response() res, @Param('id') id) { this.usersService.getUser(+id) .then((user) => res.status(HttpStatus.OK).json(user)); } @Post() addUser(@Response() res, @Body('user') user) { this.usersService.addUser(user) .then((msg) => res.status(HttpStatus.CREATED).json(msg)); } }
这很简单,是么?
在这一刻,咱们的应用程序甚至还未工做。
为什么?由于 Nest 不知道有关 UsersService
的任何内容。此组件不是 ApplicationModule
的一部分,咱们必须在那里添加:
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [ UsersController ], components: [ UsersService ], }) export class ApplicationModule {}
如今咱们的应用程序将执行,但仍然有一个路由不能正常工做,就是 addUser
。为何?由于咱们正在尝试解析请求体(req.body.user
),而没有使用 express
的 body-parser
中间件。正如你知道的,能够经过 express
实例做为 NestFactory.create()
方法的第二个参数。
让咱们安装插件:
$ npm install --save body-parser
而后在咱们的 express
实例中设置它。
import express from 'express'; import * as bodyParser from 'body-parser'; import { NestFactory } from '@nestjs/common'; import { ApplicationModule } from './modules/app.module'; const instance = express(); instance.use(bodyParser.json()); const app = NestFactory.create(ApplicationModule, instance); app.listen(3000, () => console.log('Application is listening on port 3000'));
Nest 与 ES7 的 async / await
功能兼容。所以咱们能够快速重写咱们的 UsersController
:
@Controller('users') export class UsersController { constructor(private usersService: UsersService) {} @Get() async getAllUsers(@Response() res) { const users = await this.usersService.getAllUsers(); res.status(HttpStatus.OK).json(users); } @Get('/:id') async getUser(@Response() res, @Param('id') id) { const user = await this.usersService.getUser(+id); res.status(HttpStatus.OK).json(user); } @Post() async addUser(@Response() res, @Body('user') user) { const msg = await this.usersService.getUser(user); res.status(HttpStatus.CREATED).json(msg); } }
看起来更好么?在这里你能够阅读更多关于 async / await。
模块是一个带有 @Module({})
装饰器的类。该装饰器提供元数据,该框架用于组织应用程序结构。
如今,这是咱们的 ApplicationModule
:
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [ UsersController ], components: [ UsersService ], }) export class ApplicationModule {}
默认状况下,模块封装每一个依赖关系。这意味着不可能在模块以外使用其组件或控制器。
每一个模块也能够导入到另外一个模块。实际上,你应该将 Nest 模块看作是 模块树。
咱们将 UsersController
和 UsersService
移动到 UsersModule
。只需建立新文件,例如,users.module.ts
包含如下内容:
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [ UsersController ], components: [ UsersService ], }) export class UsersModule {}
而后将 UsersModule
导入到 ApplicationModule
(咱们的主应用程序):
import { Module } from '@nestjs/common'; import { UsersModule } from './users/users.module'; @Module({ modules: [ UsersModule ] }) export class ApplicationModule {}
这就是所有了。
能够看到出,使用 Nest 能够将代码天然地拆分红可分离和可重用的模块。
模块能够轻松地注入组件,看起来以下:
@Module({ controllers: [ UsersController ], components: [ UsersService, ChatGateway ], }) export class UsersModule implements NestModule { constructor(private usersService: UsersService) {} }
此外,组件还能够注入模块:
export class UsersController { constructor(private module: UsersModule) {} }