本文爲譯文,原文地址: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) {} }