nest.js中文文档:https://nest.nodejs.cn
一、概述
Nest是一个用于构建高效,可扩展的Node.js服务器端应用程序的框架。它使用渐进式JavaScript,使用TypeScript构建(保留与纯JavaScript的兼容性),并结合了OOP(面向对象编程),FP(功能编程)和FRP(功能反应编程)的元素。
二、NEST-CLI
nest.js 提供了 nest-cli
脚手架,方便快速新建新项目。
使用 nest-cli 构建基础项目 (请确保你的操作系统上安装了 Node.js(版本 >= 16)):
$ npm i -g @nestjs/cli
$ nest new project-name
新建项目之后:
$ cd project && npm install && npm run start
三、基础项目分析
SRC
app.controller.spec.ts
app.controller.ts
app.module.ts
app.service.ts
main.ts
main.ts
应用程序的条目文件。它用于NestFactory创建Nest应用程序实例。
app.service.ts
具有单一方法的基本服务。
app.module.ts
定义AppModule应用程序的根模块。
app.controller.ts
具有单一路线的基本控制器样本。
app.controller.spec.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应用程序实例,我们正在使用NestFactory
。NestFactory
它是最基础的类之一,它公开了一些允许创建应用程序实例的静态方法。该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 service
和cat 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()
装饰提供了元数据
每个应用程序至少有一个模块,一个根模块。根模块是Nest开始安排应用程序树的地方。实际上,根模块可能是应用程序中唯一的模块,尤其是当应用程序很小的时候。然而,对于大型应用程序,它没有意义。在大多数情况下,您将拥有多个模块,每个模块都具有密切相关的功能集。
所述@Module()
装饰采用单个对象,其属性描述该模块:
providers | 将由Nest注入器实例化的提供程序,并且至少可以在此模块之间共享。 |
---|---|
controllers | 必须创建的控制器集 |
imports | 导出此模块中所需的提供程序的导入模块列表 |
exports | 其子集providers由此模块提供,并应在其他模块中可用 |
上一节中CatsController
与CatsService
属于同一应用程序域。我们将考虑将它们移动到一个特征模块,即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 {}
全局模块只能注册一次,注册之后,无需再次引用便可使用。
八、数据库
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 {}