nest.js从入门到放弃

官方文档 https://docs.nestjs.com

一、概述
Nest是一个用于构建高效,可扩展的Node.js服务器端应用程序的框架。它使用渐进式JavaScript,使用TypeScript构建(保留与纯JavaScript的兼容性),并结合了OOP(面向对象编程),FP(功能编程)和FRP(功能反应编程)的元素。

二、NEST-CLI
nest.js 提供了 nest-cli 脚手架,方便快速新建新项目。
使用 nest-cli 构建基础项目:

$ npm i -g @nestjs/cli
$ nest new project-name

新建项目之后:

$ cd project
$ npm install
$ npm run start

三、先决条件
请确保您的操作系统上安装了 node.js(> = 8.9.0)。

四、基础项目分析

SRC
  app.controller.ts
  app.module.ts
  main.ts

main.ts 应用程序的条目文件。它用于NestFactory创建Nest应用程序实例。
app.module.ts 定义AppModule应用程序的根模块。
app.controller.ts 具有单一路线的基本控制器样本。

import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(ApplicationModule);
  await app.listen(3000);
}
bootstrap();

要创建Nest应用程序实例,我们正在使用NestFactoryNestFactory它是最基础的类之一,它公开了一些允许创建应用程序实例的静态方法。该create()方法返回一个实现INestApplication接口的对象,并提供一组可用的方法。

五、运行

npm run start

六、控制器(controller)
控制器负责处理传入的请求并将响应返回给客户端。
控制器的目的是接收应用程序的特定请求。在路由该控制器接收用于请求机构的控制。通常,每个控制器具有多个路由,并且不同的路由可以执行不同的动作。

为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,并使Nest能够创建路由映射(将请求绑定到相应的控制器)。

在下面的示例中,我们将使用定义基本控制器所需的 @Controller()装饰器。我们将指定一个可选的前缀。在Controller装饰器中使用前缀允许我们避免在路径可能共享公共前缀时重复自己。

cats.controller.ts

import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}

可以使用CLI提供的快捷命令创建控制器:

$ nest g controller cats

所述@Get()的前装饰findAll()方法告诉NEST创建此特定路线路径的端点并映射到该处理程序的每个相应的请求。由于我们已经为每个route(cats)声明了一个前缀,因此Nest会将每个/catsGET请求映射到此方法。

请求对象

许多端点需要访问客户端请求详细信息。实际上,Nest使用特定于库(默认情况下为express)的请求对象。因此,我们可以强制Nest使用@Req()装饰器将请求对象注入到处理程序中。

cats.controller.ts

import { Controller, Get, Req } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request) {
    return 'This action returns all cats';
  }
}

可以使用专用的装饰器,例如@Body()or @Query(),它们是开箱即用的。

下面展示了nest装饰器对象express中对象的对应关系

nest装饰器 express 对象
@Request() req
@Response() res
@Next() next
@Session() req.session
@Param(param?: string) req.params / req.params[param]
@Body(param?: string) req.body / req.body[param]
@Query(param?: string) req.query / req.query[param]
@Headers(param?: string) req.headers / req.headers[param]

新增一个post接口:

cats.controller.ts

import { Controller, Get, Post } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Post()
  create() {
    return 'This action adds a new cat';
  }

  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}

路由通配符

@Get('ab*cd')
findAll() {
  return 'This route uses a wildcard';
}

上述路线路径匹配abcd,ab_cd,abecd,等等。

状态码
默认情况下,响应状态代码始终为200,但POST请求为201。我们可以通过@HttpCode(...)在处理程序级别添加装饰器来轻松更改此行为。


要指定自定义响应标头,您可以使用 @Header()装饰器或特定于库的响应对象。

路径参数
需要接受动态数据作为URL的一部分时,具有静态路径的路由无济于事。为了定义带参数的路径,我们可以直接在路径路径中特定路由参数

@Get(':id')
findOne(@Param() params) {
  console.log(params.id);
  return `This action returns a #${params.id} cat`;
}

async await
每个异步函数都必须返回一个Promise。这意味着您可以返回Nest能够自行解决的延迟值。

@Get()
async findAll(): Promise<any[]> {
  return [];
}

七、服务(service)
创建一个简单的CatsService provider开始。

cats.service.ts JS

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

nest-cli提供的快捷命令:$ nest g service cats/cats

然后就可以把service引入到controller中使用

cats.controller.ts

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

依赖注入

Nest是围绕通常称为依赖注入的强大设计模式构建的。

在Nest中,由于TypeScript功能,它非常容易管理依赖项,因为它们只是按类型解析,然后传递给控制器​​的构造函数:

constructor(private readonly catsService: CatsService) {}

之后将新建的cat servicecat controller引入app module

app.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class ApplicationModule {}

八、模块(module)
模块是用@Module()装饰器注释的类。的@Module()装饰提供了元数据

module示例

每个应用程序至少有一个模块,一个根模块。根模块是Nest开始安排应用程序树的地方。实际上,根模块可能是应用程序中唯一的模块,尤其是当应用程序很小的时候。然而,对于大型应用程序,它没有意义。在大多数情况下,您将拥有多个模块,每个模块都具有密切相关的功能集。

所述@Module()装饰采用单个对象,其属性描述该模块:

providers 将由Nest注入器实例化的提供程序,并且至少可以在此模块之间共享。
controllers 必须创建的控制器集
imports 导出此模块中所需的提供程序的导入模块列表
exports 其子集providers由此模块提供,并应在其他模块中可用

上一节中CatsControllerCatsService属于同一应用程序域。我们将考虑将它们移动到一个特征模块,即CatsModule

cats/ cats.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

nest-cli提供的快捷命令:$ nest g module cats

我们定义了cats.module.ts文件,然后将与此模块相关的所有内容移动到cats目录中。我们需要做的最后一件事是将此模块导入根模块ApplicationModule

app.module.ts

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule {}

模块重新导出

@Module({
  imports: [CommonModule],
  exports: [CommonModule],
})
export class CoreModule {}

全局模块

import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}

全局模块只能注册一次,注册之后,无需再次引用便可使用。

九、中间件

十、异常处理



N、数据库
Nest附带了随时可用的@nestjs/typeorm软件包TypeORM,官方说绝对是迄今为止最成熟的对象关系映射器(ORM)
安装依赖 $ npm install --save typeorm mysql
新建database文件夹
新建batabase.providers.ts

database.providers.ts

import { createConnection } from 'typeorm';

export const databaseProviders = [
  {
    provide: 'DbConnectionToken',
    useFactory: async () => await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [
          __dirname + '/../**/*.entity{.ts,.js}',
      ],
      synchronize: true,
    }),
  },
];

新建database.module.ts

database.module.ts

import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';

@Module({
  providers: [...databaseProviders],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

存储库模式
新建photo文件夹
新建photo.entity.ts

photo/ photo.entity.ts

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 500 })
  name: string;

  @Column('text')
  description: string;

  @Column()
  filename: string;

  @Column('int')
  views: number;

  @Column()
  isPublished: boolean;
}

新建photo.providers.ts

photo.providers.ts

import { Connection, Repository } from 'typeorm';
import { Photo } from './photo.entity';

export const photoProviders = [
  {
    provide: 'PhotoRepositoryToken',
    useFactory: (connection: Connection) => connection.getRepository(Photo),
    inject: ['DbConnectionToken'],
  },
];

现在,我们可以注入PhotoRepository的到PhotoService用的@Inject()装饰:
新建photo.service.ts

photo.service.ts

import { Injectable, Inject } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Photo } from './photo.entity';

@Injectable()
export class PhotoService {
  constructor(
    @Inject('PhotoRepositoryToken')
    private readonly photoRepository: Repository<Photo>,
  ) {}

  async findAll(): Promise<Photo[]> {
    return await this.photoRepository.find();
  }
}

新建photo.module.ts

photo.module.ts

import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { photoProviders } from './photo.providers';
import { PhotoService } from './photo.service';

@Module({
  imports: [DatabaseModule],
  providers: [
    ...photoProviders,
    PhotoService,
  ],
})
export class PhotoModule {}

最后导入根模块app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module'
import { PhotoModule } from './photo/photo.module'
@Module({
  imports: [CatsModule,PhotoModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342