【譯】基於 TypeScript 的 Node.js 框架 Nest 正式版發佈!(上)

本文爲譯文,原文地址: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 創建在著名倉庫 Expresssocket.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 (decoratorsasync / await)功能構建。這意味着,使用它的最簡單的方法是 BabelTypeScripttypescript

在本文中,我將使用 TypeScript(它不是必須的!),我推薦你們選擇這種方式。示例文件 tsconfig.json 以下:數據庫

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": false,
    "noImplicitAny": false,
    "noLib": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es6"
  },
  "exclude": [
    "node_modules"
  ]
}

記住 emitDecoratorMetadataexperimentalDecorators 必須設置爲 trueexpress

讓咱們從頭開始咱們的應用程序。首先,咱們必須被咱們的應用程序建立入口模塊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 實例

若是要徹底控制 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'));

這意味着,你能夠直接添加一些自定義配置(例如,設置一些插件,如 morganbody-parser)。

控制器(Controllers)

控制層(Controllers)負責處理傳入的 HTTP 請求。在 Nest 中,控制器是一個帶有 @Controller() 裝飾器的類。

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 數組中,這就夠了。

組件(Components)

幾乎全部的東西都是組件,ServiceRepositoryProvider等等。而且他們能夠經過構造函數注入控制器或另外一組件

components

在上一節中, 咱們構建了一個簡單的 controllerUsersController。這個 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),而沒有使用 expressbody-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'));

Async / await

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

模塊(Modules)

模塊是一個帶有 @Module({}) 裝飾器的類。該裝飾器提供元數據,該框架用於組織應用程序結構。

modules

如今,這是咱們的 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 模塊看作是 模塊

咱們將 UsersControllerUsersService 移動到 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) {}
}

下篇內容連接: http://www.javashuo.com/article/p-mqpqlfgd-md.html

相關文章
相關標籤/搜索