nestjs学习

Node框架之NestJS入门学习 文章主要分为三部分:1 NestJS 与Egg对比2 NestJS 基础知识介绍3 使用NestJS快速搭建应用,实现CURD操作e-link文档Egg官网NestJS 官网NestJS中文文档(非官方) 阅读详情

某种原因开始学习nestjs,想用nestjs做后端,mongodb做数据库来实现一个微信小程序的后台,开始了哼哧哼哧的爬代码之路。

如果想使用自己写的js或ts工具库,需要使用require进行导入!!否则找不到文件!


nestjs是一个nodejs的服务端app框架,完全支持ts,借鉴springmvc和angular,底层采用express和fastify

文档:https://nestjs.bootcss.com/

安装

npm i -g @nestjs/cli
nest new xxx
// 启动
npm run start:dev
// 调试,同时配置vscode的launch.json,先启动应用后启动调试即可
npm run start:debug
// launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Attach NestJS WS",
      "port": 9229,
      "restart": true,
      "stopOnEntry": false,
      "protocol": "inspector"
    }
  ]
}

目录介绍

  1. main入口文件,用于配置一些全局起作用的东西,在这里绑定端口
  2. controller文件,相当于路由,这里配置各个地址映射到的函数,注意构造函数变量会自动注入,需要@Controller()
  3. service文件,主要写业务逻辑,这个也可以给别的模块进行逻辑复用,需要@Injectable
  4. module文件,主要用于配置当前模块的关联关系,比如引入其他模块,或导出当前模块

cli命令

nest g --help

    ┌───────────────┬─────────────┬──────────────────────────────────────────────┐
    │ name          │ alias       │ description                                  │
    │ application   │ application │ Generate a new application workspace         │
    │ class         │ cl          │ Generate a new class                         │
    │ configuration │ config      │ Generate a CLI configuration file            │
    │ controller    │ co          │ Generate a controller declaration            │
    │ decorator     │ d           │ Generate a custom decorator                  │
    │ filter        │ f           │ Generate a filter declaration                │
    │ gateway       │ ga          │ Generate a gateway declaration               │
    │ guard         │ gu          │ Generate a guard declaration                 │
    │ interceptor   │ itc         │ Generate an interceptor declaration          │
    │ interface     │ itf         │ Generate an interface                        │
    │ middleware    │ mi          │ Generate a middleware declaration            │
    │ module        │ mo          │ Generate a module declaration                │
    │ pipe          │ pi          │ Generate a pipe declaration                  │
    │ provider      │ pr          │ Generate a provider declaration              │
    │ resolver      │ r           │ Generate a GraphQL resolver declaration      │
    │ service       │ s           │ Generate a service declaration               │
    │ library       │ lib         │ Generate a new library within a monorepo     │
    │ sub-app       │ app         │ Generate a new application within a monorepo │
    │ resource      │ res         │ Generate a new CRUD resource                 │
    └───────────────┴─────────────┴──────────────────────────────────────────────┘

常用的就是nest g res 模块名,可以生成CRUD模板,包括:controller,service,module

RESTful风格

提一嘴,该风格主要是将请求类型利用起来了,然后url传参以param传参,而不是query传参

// 普通
/api/get?id=123
// restful
/api/get/123

请求类型常见的对应:

GET 查询
POST 提交
PUT PATCH 更新
DELETE 删除

API版本控制

nestjs提供三种版本控制方案:

  1. uri中带版本号(常用)
  2. header中带
  3. accept中带

启用方式:

// 1.在main中,利用app实例进行配置
app.enableVersioning({
  type: VersioningType.URI,
})
// 2.controller中配置当前路由版本
@Controller({
  path:"user",
  version:'1'
})
// 也可以在具体映射函数时配置
@Get()
@Version('1')
findAll() {
  return this.userService.findAll();
}

http code规范

200 OK
304 Not Modified 协商缓存了
400 Bad Request 参数错误
401 Unauthorized token错误
403 Forbidden referer origin 验证失败
404 Not Found 接口不存在
500 Internal Server Error 服务端错误
502 Bad Gateway 上游接口有问题或者服务器问题

session

nestjs启用session,底层用的express

npm i express-session --save
npm i @types/express-session -D

然后在main中引入并use

import * as session from 'express-session'
...
app.use(session({ secret: "xiaopi3", name: "xp.session", rolling: true, cookie: { maxAge: null } }))

session()参数解释:

secret		生成session签名
name		生成客户端cookie 的名字 默认 connect.sid
cookie		设置返回到前端 key 的属性,默认值为{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }
rolling		在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)

controller

控制器中的函数可以使用的参数和注解:

@Request()		req          
@Response()		res		        
@Next()			next
@Session()		req.session
@Param(key?: string)	req.params/req.params[key]
@Body(key?: string)		req.body/req.body[key]
@Query(key?: string)	req.query/req.query[key]
@Headers(name?: string)   	req.headers/req.headers[name]
@HttpCode

注意:如果在路由中使用@Response()获取response,则输出会重定向到response中,方法中的return值无效,且如果不调用response.write()则请求会被挂起,无法继续

参考用例:

@Get()
find(@Query() query) {
  console.log(query)
  return { code: 200 }
}

service

service文件中的类用@Injectable注解,是一个provider,在module文件的@module()中的provider配置项中进行配置,然后在controller中的构造注入就可以使用了
provider用于沟通第三方包,在这里可以通过构造函数的注入,来使用第三方包提供的功能

// 1. 定义provider app.service.ts
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}
// 2.在module中配置 app.module.ts
@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

// 3. 在controller构造注入 app.controller.ts
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

共享service

在module中使用字段exports来共享当前的service,以便其他模块可以使用

@Module({
	imports: [HttpModule],
	controllers: [UsersController],
	providers: [UsersService],
	exports:[UsersService]
})
export class UsersModule{}

provider

用@Injectable注解的类称为provide,可以用于在controller构造中进行注入,来进行使用。

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [
  // 1. 常见用法
  AppService,
  // 2. 可以给provide 自定义名称,并在注入时指定【相同名称】
  {provide:"appsvc",useClass:AppService},
  // 3. 可以配置自定义provide值
  {provide:"APP",useValue:["A","B","C"]},
  // 4. 使用工厂函数,将provide加工,返回一个新的值,会包装成provide使用,可以返回promise,可异步
  { provide: 'MyAPP', inject: ['APP',AppService], useFactory(APP) { return APP } }],
  ],
  
})
export class AppModule {}

// controller 构造中,对于名称和provider中的service一样的(驼峰),可以不用@Inject表明注入的名称
private readonly appService: AppService,
@Inject('appsvc') private readonly appService: AppService,
@Inject('APP') private readonly applist: string[],
@Inject('MyAPP') private MyAPP:any

module

module文件用于配置当前模块和其他模块的关系

  1. 可以引入其他模块:引入后可以在当前模块中调用被引入的模块service(需要controller构造注入)
  2. 可以导出当前模块的服务给其他模块使用
@Module({
	imports: [HttpModule],
	controllers: [UsersController],
	providers: [UsersService],
	exports:[UsersService]
})
export class UsersModule{}

全局模块:@Global()

这种模块在使用时,无需在别的module文件中导入,即可在其controller文件中的构造注入

动态模块:DynamicModule

模块依赖参数,则可以在该模块中定义一个静态方法,去接收参数,返回一个DynamicModule类型的对象

// module定义
interface Options {
    path: string
}
 
@Global()
@Module({
})
export class ConfigModule {
    static forRoot(options: Options): DynamicModule {
        return {
            module: ConfigModule,
            providers: [
                {
                    provide: "Config",
                    useValue: { baseApi: "/api" + options.path }
                }
            ],
            exports: [
                {
                    provide: "Config",
                    useValue: { baseApi: "/api" + options.path }
                }
            ]
        }
    }

// 引入该动态模块
@Module({
	imports: [ConfigModule.forRoot({path: '/abc'})],
	controllers: [UsersController],
	providers: [UsersService]
})
export class UsersModule{}

中间件

在路由处理之前进行调用,可访问请求和响应对象,需要注意,如果当前中间件没有结束请求响应的逻辑,则必须调用next(),否则请求被挂起

创建中间件:中间件是继承于NestMiddleware的service,其中的use方法即为该中间件处理的逻辑函数,包含三个参数,请求响应和next

使用中间件:中间件需要在module中配置(逻辑作用于路由前),configure方法apply中间件

// 创建中间件service
import {Injectable,NestMiddleware } from '@nestjs/common'
import {Request,Response,NextFunction} from 'express'

@Injectable()
export class Logger implements NestMiddleware{
  use (req:Request,res:Response,next:NextFunction) {
    console.log(req)
    next()
  }
}

// 使用中间件
import { Module,NestModule,MiddlewareConsumer } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { Logger } from 'src/middleware';
@Module({
  controllers: [UserController],
  providers: [UserService],
  exports:[UserService]
})
export class UserModule implements NestModule{
  configure (consumer:MiddlewareConsumer) {
    // 配置方式有如下三种:可以指定中间件作用路由范围
    consumer.apply(Logger).forRoutes('user')
    consumer.apply(Logger).forRoutes({path:'user',method:RequestMethod.GET})
    consumer.apply(Logger).forRoutes(UserController)
  }
}

全局中间件

全局和普通中间件不同,全局中间件是一个函数,在app.use中配置

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
 
const whiteList = ['/list']
 
function middleWareAll  (req,res,next) {
     console.log(req.originalUrl,'我收全局的')
     if(whiteList.includes(req.originalUrl)){
         next()
     }else{
         res.send('小黑子露出鸡脚了吧')
     }
}
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(middleWareAll)
  await app.listen(3000);
}
bootstrap();

第三方中间件CORS

举例:使用cors中间件解决跨域问题

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as cors from 'cors'
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(cors())
  await app.listen(3000);
}
bootstrap();

multer中间件(文件上传使用)

multer专门用于处理表单数据multipart/form-data类型的

  1. 注入MulterModule到模块
import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname, join } from 'path';

@Module({
  imports: [MulterModule.register({
    // dest:'./uploadFiles',
    storage:diskStorage({
      destination:join(__dirname,"/uploadFiles"),
      filename(req, file, callback) {
        const filename = `${new Date().getTime() + extname(file.originalname)}`
        return callback(null,filename);
      },
    })
  })],
  controllers: [UploadController],
  providers: [UploadService]
})
export class UploadModule {}

注意:dest和storage两个属性取其一即可,如果不需要对文件做改名的其他操作,可以直接使用dest传入字符串即可
其中:
./uploadFiles表示上传目录为项目根目录(项目根目录/uploadFiles)。
join(__dirname,"../uploadFiles")表示上传文件为运行时的该js文件所属目录上一级的uploadfiles中(项目根目录/dist/uploadFiles)

如果是打包使用,这两种都一样
如果是开发使用,需要注意

  1. controller中的方法配置file拦截器
@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @UseInterceptors(FileInterceptor('pic'))
  @Post('picture')
  uploadPic(@UploadedFile() file): ResponseInter{
    console.log(file)
    return {
      code: 200,
      message: "上传文件成功!"
    }
  }
}

其中@UseInterceptors(FileInterceptor('pic'))的pic表示提取上传表单中的pic字段作为文件去解析

  1. 生成静态目录提供直接访问和下载
...
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.useStaticAssets(join(__dirname, "/uploadFiles"), {
    prefix: "/file" // 访问文件时的url地址,自定义
  })
...

// http://localhost:3000/file/文件名 即可下载

注意:需要指定app类型为NestExpressApplication否则ts报错
静态文件目录可以使用字符串也可以使用join去生成,但两者稍有差别

  • 字符串:和multer上传目录保持一致即可
  • join:由于所属js文件层级不同,需要考虑当前目录层级

文件下载

  1. 使用response.download方法下载
@Get('download')
downLoad(@Res() res: Response) {
  const url = join(__dirname, '../uploadFiles/1671437555790.jpg')
  res.download(url)
}
  1. 打包后下载
    打包使用compressing包提供的zip.stream类
@Get('zip')
async down(@Res() res: Response) {
  const url = join(__dirname, '../uploadFiles/1671437555790.jpg')
  const stream = new zip.Stream()
  await stream.addEntry(url)

  res.setHeader('Content-Type', 'application/octet-stream');
  res.setHeader('Content-Disposition', `attachment; filename=download.zip`);

  stream.pipe(res)
}

注意:后端如果返回的是流数据,前端如果想实现点击下载功能,需要将流放入Blob中

const useFetch = async (url: string) => {
  const res = await fetch(url).then(res => res.arrayBuffer())
  console.log(res)
  const a = document.createElement('a')
  a.href = URL.createObjectURL(new Blob([res],{
    // type:"image/png"
  }))
  a.download = 'donwload.zip' // 这里可以自定义名称,也可以获取header中的文件名
  a.click()
}
 
const download = () => {
  useFetch('http://localhost:3000/upload/zip')
}

拦截器

用于在函数前或后做一些其他操作,比如转换结果,抛出异常等

响应拦截器

在响应前后进行拦截,修改response数据

  1. 新建一个拦截器类,设置成可注入的服务
import { Injectable, NestInterceptor, CallHandler } from '@nestjs/common'
import { map } from 'rxjs/operators'
import {Observable} from 'rxjs'

interface data<T>{
    data:T
}
@Injectable()
export class STDResponse<T = any> implements NestInterceptor {
    intercept(context, next: CallHandler):Observable<data<T>> {
        return next.handle().pipe(map(data => {
            return {
               data,
               status:200,
               success:true,
               message:"成功"
            }
        }))
    }
}
  1. 在main中use这个拦截器
app.useGlobalInterceptors(new STDResponse())

过滤器

在这里插入图片描述

用于过滤一些数据,常用于异常过滤
nestjs内建异常过滤器层,用于处理未捕获的HttpException异常
注意:当异常不属于http异常时,默认返回:

{
  "statusCode": 500,
  "message": "Internal server error"
}

全局异常过滤器支持http-errors库,尽可能在异常发生时提供状态码和异常原因是一个好习惯

{
    "data": "Cannot GET /111",
    "time": "2022/12/20 16:29:18",
    "success": false,
    "path": "/111",
    "status": 404
}
  1. 定义异常过滤器
 
import { ExceptionFilter, Catch, ArgumentsHost,HttpException } from '@nestjs/common'
import {Request,Response} from 'express'
 
@Catch(HttpException)
export class ErrResponse implements ExceptionFilter {
    catch(exception:HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp()
        const request = ctx.getRequest<Request>()
        const response = ctx.getResponse<Response>()
 
        const status = exception.getStatus()
 
        response.status(status).json({
           data:exception.message,
           time:new Date().toLocaleString(),
           success:false,
           path:request.url,
           status
        })
    }
}
  1. 全局使用
app.useGlobalFilters(new ErrResponse())
  1. 模块路由使用
@UseFilters(HttpExceptionFilter)
export class CatsController {}
  1. 路由方法使用
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}

注意:模块和路由使用时不用传递new ErrResponse(),可以直接传递类即可

如果一个模块需要使用一个全局过滤器,而该过滤器由于已经在app中注册过了,所以无法直接在模块中进行usefilter,可以通过providers来引入:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

更多使用方法,参考文档:https://docs.nestjs.com/exception-filters

管道

管道功能有两个:数据转换、数据验证
管道要么返回原数据(校验通过),要么抛出异常(校验失败)
nestjs提供8个内置管道

ValidationPipe
ParseIntPipe
ParseFloatPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
ParseEnumPipe
DefaultValuePipe

示例:

// 将入参中的id转换成整数
// 如果转换失败,会直接抛出异常,该异常可以被过滤器捕获
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

// 校验uuid,可以传递参数来指定uuid版本
@Get(':uuid')
async findOne(@Param('uuid', new ParseUUIDPipe()) uuid: string) {
  return this.catsService.findOne(uuid);
}

注意:管道可以直接传类,也可以传递实例,后者可以方便的传递一些参数来初始化管道,前者使用默认参数

自定义管道

  • 管道文件
cd 模块
nest g pi 管道名称

将管道文件放置在某个模块下去验证该模块的某个接口数据交换格式信息

DTO:数据传输对象,即前后端交互时的数据承载体,可用于校验数据格式规范

标准管道文件格式:

import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

自定义管道可修改部分即transform函数体内的逻辑
value:流进管道的数据
metadata:校验DTO信息,包括以下:

export interface ArgumentMetadata {
  type: 'body' | 'query' | 'param' | 'custom';
  metatype?: Type<unknown>;
  data?: string;
}

详情官网有解释:
在这里插入图片描述

DTO校验

DTO校验有两种:

  • 基于schema校验
  • 基于class校验

校验方式不同,管道文件编写和路由中的使用方式也不同

  1. 基于class校验
    需要安装校验包和类转换包,且必须使用ts编写app
    npm i --save class-validator class-transformer
// DTO类
import { IsString, IsInt } from 'class-validator';
export class CreateCatDto {
  @IsString()
  name: string;
  @IsInt()
  age: number;
  @IsString()
  breed: string;
}

// 管道
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

// 路由
@Post()
async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto,) {
  this.catsService.create(createCatDto);
}
  1. 基于schema校验
    需要安装schema生成包
    npm install --save joi
// 创建schema
略
// 管道,构造中传入需要校验的schema
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ObjectSchema } from 'joi';

@Injectable()
export class JoiValidationPipe implements PipeTransform {
  constructor(private schema: ObjectSchema) {}

  transform(value: any, metadata: ArgumentMetadata) {
    const { error } = this.schema.validate(value);
    if (error) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }
}

// 路由,传入schema
@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

全局DTO验证管道

nestjs提供一个默认的全局的

import {ValidationPipe} from '@nestjs/common'
...
app.useGlobalPipes(new ValidationPipe());
...

默认值给管道

如果管道验证的值为空,则肯定抛出异常,可以提供默认值,防止发生

@Get()
async findAll(
  @Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
  @Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
  return this.catsService.findAll({ activeOnly, page });
}

守卫

执行顺序:中间件–>守卫–>过滤器–>管道

守卫返回为true(放行)、false(阻挡)

守卫有一个单独的责任。它们根据运行时出现的某些条件(例如权限,角色,访问控制列表等)来确定给定的请求是否由路由处理程序处理。这通常称为授权。在传统的 Express 应用程序中,通常由中间件处理授权(以及认证)。中间件是身份验证的良好选择,因为诸如 token 验证或添加属性到 request 对象上与特定路由(及其元数据)没有强关联。
————————————————
版权声明:本文为优快云博主「小满zs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq1195566313/article/details/127175529

新建一个守卫文件:nest g gu 名称

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

在路由类上加上@UseGuards(AuthGuard)即可

守卫参数

在controller中,某个路由需要通过参数控制访问时,可以通过SetMetadata 装饰器来实现

@SetMetadata(参数1,参数2):第一个参数为key,第二个参数自定义,可通过反射方法获取到该参数,进行判断。

@SetMetadata('roles', ['admin'])
@Get()
findAll() {
  return this.tmpService.findAll();
}

在守卫中可以对传入的参数进行处理

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core'
import type { Request } from 'express'
@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private Reflector: Reflector) { }
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const admin = this.Reflector.get<string[]>('role', context.getHandler())
    const request = context.switchToHttp().getRequest<Request>()
    if (admin.includes(request.query.role as string)) {
      return true;
    }else{
      return false
    }
  }
}

测试:/tmp?role=admin

全局守卫

app.useGlobalGuards(new AuthGuard())

装饰器

装饰器文件用于生成自定义装饰器,封装一些小功能
新建:nest g d 装饰器名称

// 模板
import { SetMetadata } from '@nestjs/common';

export const Mydec = (...args: string[]) => SetMetadata('mydec', args);

在路由中使用

// @SetMetadata('roles', ['admin'])
@Mydec('admin')
@Get()
findAll() {
  return this.tmpService.findAll();
}

自定义装饰器相当于一个封装了的@SetMetadata装饰器,可以被守卫捕获其中的内容,然后做处理

自定义参数装饰器

放在参数前,对参数进行赋值

  1. 普通的无参数类型
// 装饰器文件
// 参数装饰器,从request中获取user返回。(赋值给参数)
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

// 路由
@Get()
async findOne(@User() user: UserEntity) {
  console.log(user);
}
  1. 有参数类型
// 装饰器,第一个参数为路由传过来的参数
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    return data ? user?.[data] : user;
  },
);
// 路由,装饰器函数接收一个参数
@Get()
async findOne(@User('firstName') firstName: string) {
  console.log(`Hello ${firstName}`);
}

swagger

使用swagger可以在编写代码时对方法或类使用swagger注解,可以在生成的在线文档中查看,且可以调用

参考文档:https://docs.nestjs.com/openapi/introduction

需要引入两个包:npm install @nestjs/swagger swagger-ui-express

在main中注册swagger:

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  const options = new DocumentBuilder().setTitle('接口文档').setDescription('描述').setVersion('1').build()
  const document = SwaggerModule.createDocument(app,options)
  SwaggerModule.setup('/api-docs',app,document)
  await app.listen(3000);
}
bootstrap();

访问:localhost:3000/api-docs

常用注解

注解作用在controller文件中

对路由类描述:@ApiTags('守卫相关接口')
对方法描述:@ApiOperation({summary:"测试admin",description:"请求该接口需要amdin权限"})
对参数描述:

@ApiParam({name:"id",description:"用户id",required:true})
@ApiQuery({name:"xxxx",description:"bbb"})

对body描述(DTO):

import { ApiProperty } from "@nestjs/swagger"
 
export class CreateGuardDto {
    @ApiProperty({ description: "姓名", example: "小满" })
    name: string
    @ApiProperty({ description:"年龄"})
    age: number
}

对路由返回值描述:@ApiResponse({status:403,description:"自定义返回信息"})

jwt测试

使用一些有关权限接口时需要传递token等

const options = new DocumentBuilder().addBearerAuth().setTitle('接口文档').setDescription('描述').setVersion('1').build()

在需要token的接口,写上注解:@ApiBearerAuth()

@ApiBearerAuth()
@Controller('tmp')
export class TmpController{}

ORM

可以选择任何一个nodejs的orm库,nestjs也集成了三个常用的:@nestjs/typeorm ,@nestjs/sequelize,@nestjs/mongoose

typeorm

  1. 先安装依赖:npm install --save @nestjs/typeorm typeorm mysql2
  2. 在app.module.ts中注入数据库
@Module({
  imports: [
      TypeOrmModule.forRoot({
      type: "mysql", //数据库类型
      username: "root", //账号
      password: "123456", //密码
      host: "localhost", //host
      port: 3306, //
      database: "mydb", //库名
      // entities: [__dirname + '/**/*.entity{.ts,.js}'], //实体文件,如果使用autoLoadEntities:true,就不要使用这个了
      synchronize:true, //synchronize字段代表是否自动将实体类同步到数据库,对实体类的修改会影响数据库
      retryDelay:500, //重试连接数据库间隔
      retryAttempts:10,//重试连接数据库的次数
      autoLoadEntities:true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
      }),
      UsersModule, UploadModule, TmpModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

注意:synchronize:true在生产模式要改成false

  1. 在某个需要连接数据库的模块中,定义实体(@Entity()注解不要忘了)
import {Entity,Column,PrimaryGeneratedColumn} from 'typeorm'

@Entity()
export class User {
   //自增列
   @PrimaryGeneratedColumn()
   id:number
   //普通列
   @Column()
   name:string
}
  1. 在该模块注入实体模块(可以注入多个)
import {User} from './entities/user.entity'
@Module({
  imports: [
    TypeOrmModule.forFeature([User])
    HttpModule
  ],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

entity装饰器

// 普通自增主键
@PrimaryGeneratedColumn()
id:number
// uuid自增
@PrimaryGeneratedColumn("uuid")
id:number
// 生成uuid
@Generated('uuid')
uuid:string
// 限制字段类型和长度
@Column({type:"varchar",length:200})
password: string
// 自动创建时间戳
@CreateDateColumn({type:"timestamp"})
create_time:Date
// 枚举
@Column({
  type:"enum",
  enum:['1','2','3','4'],
  default:'1'
})
xx:string
// 数组(数据库中拼接逗号)
@Column("simple-array")
names: string[];
// json(数据库中转成json字符串)
@Column("simple-json")
profile: { name: string; nickname: string };

更多装饰器功能参考:https://xiaoman.blog.youkuaiyun.com/article/details/127218592

示例

  1. APP配置数据库
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "mysql", //数据库类型
      username: "root", //账号
      password: "xxx", //密码
      host: "xxx.xxx.xxx.xxx", //host
      port: 3306, //
      database: "xxxx", //库名
      synchronize: true, //synchronize字段代表是否自动将实体类同步到数据库,对实体类的修改会影响数据库
      retryDelay: 500, //重试连接数据库间隔
      retryAttempts: 10,//重试连接数据库的次数
      autoLoadEntities: true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
    }),UsersModule, UploadModule, TmpModule],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule { }
  1. 建立entity
import { Entity, PrimaryGeneratedColumn,Column, CreateDateColumn } from "typeorm";

@Entity()
export class Tmp {

  @PrimaryGeneratedColumn('uuid')
  uuid:string

  @Column()
  name:string

  @Column()
  age:number
  
  @CreateDateColumn({type:'timestamp'})
  createTime:Date
}
  1. 模块引入当前entity
@Module({
  imports:[TypeOrmModule.forFeature([Tmp])],
  controllers: [TmpController],
  providers: [TmpService]
})
export class TmpModule {}
  1. 新建service(引入dao层,即对应Repository)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm/dist/common';
import { Equal, Like, Repository } from 'typeorm';
import { CreateTmpDto } from './dto/create-tmp.dto';
import { UpdateTmpDto } from './dto/update-tmp.dto';
import { Tmp } from './entities/tmp.entity';

@Injectable()
export class TmpService {
  constructor(@InjectRepository(Tmp) private readonly tmp: Repository<Tmp>) { }

  create(createTmpDto: CreateTmpDto) {
    const TmpObj = new Tmp()
    TmpObj.name = createTmpDto.name;
    TmpObj.age = createTmpDto.age;
    return this.tmp.save(TmpObj);
  }

  findAll({ uuid, name, age, createTime }: Tmp) {
    const where = {}
    uuid ? where['uuid'] = Equal(uuid) : null;
    name ? where['name'] = Like(`%${name}%`) : null;
    age ? where['age'] = Equal(age) : null;
    createTime ? where['createTime'] = Equal(new Date(createTime)) : null;
    return this.tmp.find({
      where,
    });
  }
}
  1. 新建controller层
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, SetMetadata, Request, Query } from '@nestjs/common';
import { TmpService } from './tmp.service';
import { CreateTmpDto } from './dto/create-tmp.dto';
import { UpdateTmpDto } from './dto/update-tmp.dto';

@Controller('tmp')
export class TmpController {
  constructor(private readonly tmpService: TmpService) {}

  @Post()
  create(@Body() createTmpDto: CreateTmpDto) {
    return this.tmpService.create(createTmpDto);
  }

  @Get()
  findAll(@Query() query) {
    return this.tmpService.findAll(query);
  }
}

mongoose

连接mongodb可以使用typeorm,也可以使用mongoose包:npm i @nestjs/mongoose mongoose

  1. APP配置数据库
@Module({
  imports: [
    MongooseModule.forRoot('mongodb://xxx:xxxx@xxxxx:27017/xxxxx'), UsersModule, UploadModule, TmpModule],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule { }

注意:forRoot()和mongoose.connect()一样,接收一个数据库配置的对象

  1. 创建schema(类似于entity)
    mongoose使用schema来配置实体和数据库的映射,可以使用nestjs的装饰器创建,也可以使用mongoose手动创建

Note you can also generate a raw schema definition using the DefinitionsFactory class (from the nestjs/mongoose).

@Schema()
// user.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';

export type UserDocument = HydratedDocument<User>;

// 这里注意了,`@Schema()`这个装饰器会默认把类名加s作为数据库集合名称,可以手动指定名称,versionKey用于控制生成的数据是否携带数据版本号字段
@Schema({ versionKey: false, collection: 'user' })
export class User {
  @Prop({ required: true })
  username: string;

  @Prop({ required: true })
  openid: string;

  @Prop({ required: true })
  role: string;
}

export const UserSchema = SchemaFactory.createForClass(User)

注意:schema导出了两个对象,一个类型,一个schema

这里注意,如果非基础类型且有引用别的model,定义方式如下:

import { Owner } from '../owners/schemas/owner.schema';

// inside the class definition
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Owner' })
owner: Owner;
// 如果是数组
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Owner' }] })
owner: Owner[];

这里注意,可以使用raw定义嵌套类型:

@Prop(raw({
  firstName: { type: String },
  lastName: { type: String }
}))
details: Record<string, any>;

如果不喜欢使用装饰器,可以使用函数来定义:

export const CatSchema = new mongoose.Schema({
   name: String,
   age: Number,
   breed: String,
});
  1. 在module中引入该shema
import { User,UserSchema } from './schemas/user.schema';

@Module({
  imports: [
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
    HttpModule
  ],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}
  1. 在service中引入dao
import { Injectable } from '@nestjs/common';
import { CreateUserDTO } from './dto/create-user.dto';
import { UpdateUserDTO } from './dto/update-user.dto';
import { User, UserDocument } from './schemas/user.schema';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User.name) private readonly userModel: Model<UserDocument>,
    private readonly httpService: HttpService
  ) { }

  async create(createUserDTO: CreateUserDTO): Promise<User> {
    const user = new User();
    user.openid = createUserDTO.openid;
    user.username = createUserDTO.username;
    user.role = createUserDTO.role;
    return this.userModel.create(user);
  }

  /** 查找所有用户 */
  async findAll(): Promise<User[]> {
    return this.userModel.find().exec();
  }

  /** 条件查找所有用户 */
  async findByConditions(conditions): Promise<User[]> {
    return this.userModel.where(conditions);
  }

  /** 查找单个用户 */
  async findOne(_id: string): Promise<User> {
    return this.userModel.findById(_id);
  }

  /** 添加单个用户 */
  async addOne(body: CreateUserDTO): Promise<void> {
    await this.userModel.create(body);
  }

  /** 编辑单个用户 */
  async editOne(_id: string, body: UpdateUserDTO): Promise<void> {
    console.log(body)
    await this.userModel.findByIdAndUpdate(_id, body);
  }

  /** 删除单个用户 */
  async deleteOne(_id: string): Promise<void> {
    await this.userModel.findByIdAndDelete(_id);
  }
  1. 在controller中使用
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDTO } from './dto/create-user.dto';
import { UpdateUserDTO } from './dto/update-user.dto';
import { User} from './schemas/user.schema';
import UserResponse from 'src/interface/response.interface';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) { }

  @Get()
  async findAll(): Promise<UserResponse<User[]>> {
    return {
      code: 200,
      data: await this.usersService.findAll(),
      message: 'success',
    };
  }
  
  @Post()
  async addOne(@Body() body: CreateUserDTO): Promise<UserResponse> {
    await this.usersService.addOne(body);
    return {
      code: 200,
      message: 'success',
    };
  }

  @Post('findByConditions')
  async findByConditions(@Body() body: CreateUserDTO): Promise<UserResponse> {
    return {
      code: 200,
      data: await this.usersService.findByConditions(body),
      message: 'success',
    };
  }

  @Patch(':_id')
  async editOne(
    @Param('_id') _id: string,
    @Body() body: UpdateUserDTO,
  ): Promise<UserResponse> {
    await this.usersService.editOne(_id, body);
    return {
      code: 200,
      message: 'Success.',
    };
  }

  @Delete(':_id')
  async deleteOne(@Param('_id') _id: string): Promise<UserResponse> {
    await this.usersService.deleteOne(_id);
    return {
      code: 200,
      message: 'Success.',
    };
  }
}

其他参考:

  • https://blog.youkuaiyun.com/weixin_45828332/article/details/114120710
  • https://docs.nestjs.com/techniques/mongodb
  • http://mongoosejs.net/docs/guide.html
2024 Nestjs入门到实战 文章目录前提-安装环境介绍 (了解即可)安装nestjs, 创建新项目, 启动新项目nestcli命令 (建议熟练掌握, 可提高开发效率)IoC(控制反转)和DI(依赖注入)概念详解IoC(控制反转)DI(依赖注入)两种思想的概念示例代码总结在nestjs中的三种依赖注入方式为什么 Controller 是单独的装饰器?@Module机制装饰器概念四种装饰器在ts中自定义装饰器类装饰器属性装饰器方法装饰器参数装饰器示例代码nestjs控制器和参数获取nest-provider基本用法Provider自定义名 阅读详情

相关推荐

NestJS 技术解析及实战指南

NestJS 是基于 Node.js 的渐进式服务端框架,使用 TypeScript 构建,融合了面向对象编程(OOP)、函数式编程(FP)和响应式编程(RP)理念。其核心设计借鉴了 Angular 的模块化思想,底层默认集成 Express(可切换为 Fastify),提供开箱即用的依赖注入、中间件管道、拦截器等企业级功能。​​核心特性示例:​​// 典型的模块定义@Module({imports: [DatabaseModule], // 依赖模块。

程序员学习园地。 1004

nestjs从零到一,快速上手(一)

【代码】nestjs从零到一,快速上手。

ben_grf的博客 1466

【nestjs】一般学习路线

文章摘要: 作者分享了自己学习NestJS的心得,通过参考开源项目nest-admin并深度阅读代码,逐步理解了NestJS的模块化设计。学习过程中,作者通过打console和添加注释来熟悉每个模块的功能。接着,作者细致阅读了NestJS的中文文档,并将文档内容与实际代码进行类比,进一步加深理解。最后,作者通过新增test模块,实践了NestJS的增删改查功能,并总结了entities、dto、service、controller和module等核心模块的作用。通过这一系列的学习和实践,作者对NestJS有

Li_Ning21的博客 681

Nodejs之Nest.js框架

Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用 JavaScript 的渐进增强的能力,使用并完全支持 TypeScript (仍然允许开发者使用纯 JavaScript 进行开发),并结合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。

在所有的事情背后,要有一颗坚定的的本心,要有一颗应对变革和提升自我的本心,世界瞬息万变,本心要坚持到底,保持学习,时刻复盘 4184

NestJS 完整技术学习路线

2. 核心接口:增(POST /todos)、删(DELETE /todos/:id)、改(PUT /todos/:id)、查(GET /todos、GET /todos/:id)◦ 请求处理:HTTP 方法装饰器(@Get/@Post/@Put/@Delete)、参数提取(@Param 路径参数、@Query 查询参数、@Body 请求体)◦ 核心属性:controllers(注册控制器)、providers(注册服务)、imports(导入依赖模块)、exports(暴露服务/模块)

学长的猫的博客 723

2025年Nestjs全网最全学习路线指南

学习此套Nestjs之前需要具备Html、 Css、 Js、TypeScript、 Nodejs、 Express基础,没有这些基础可能学不会。当然,想挑战下的话也可以直接学学Nestjs基础试试。

yuanlaile的博客 966

book-nestjs-backend:为开发者量身打造的NestJS学习利器

book-nestjs-backend:为开发者量身打造的NestJS学习利器 项目介绍 book-nestjs-backend 是一本专注于NestJS后端编程的开源项目,它包含了《NestJS로 배우는 백엔드 프로그래밍》一书中的示例代码。这本书旨在帮助开发者轻松理解和掌握NestJS的核心功能和原理,无论你是初学者还是有一定后端开发经验的开发者,都能从中受益。 项目技术分析 NestJS简...

gitblog_00533的博客 1104

微纳加工技术中的自对准硅化物(Silicide)工艺:关键步骤与应用场景解析

本文深入解析了微纳加工技术中的自对准硅化物(Silicide)工艺,详细介绍了其关键步骤与应用场景。从表面清洁、金属沉积到两步退火法,全面拆解工艺全流程,并探讨了不同技术节点下的工艺演进。文章还提供了实际应用中的问题排查方法和新兴技术对Silicide工艺的挑战,为芯片制造领域的工程师提供了实用参考。

weixin_30613727的博客 429

NestJS学习记录-NestJS任务调度

nestjs/schedule包也提供了动态API,可以帮助我们更加灵活的处理定时任务,主要通过任务的name属性交互处理任务。此导入会初始化调用任务,注册所有代码中已经存在的延期任务(Timeout)、间隔任务(Interval)和其他形式的定时任务。这样我们可以结合我们自己的场景搭建一个单独的任务调度服务,用来处理我们可能遇到的所有定时任务的需求。代码中,针对cron任务,间隔任务,延时任务分别做了增、删、停止(改)、查询。,我们可以看到cron的版本,我们项目中也要安装此版本号的cron包。

weixin_39179366的博客 989

CDR学习笔记——02

这篇写一些高级CDR的架构原理。

... 1016

Android 编译系统(Build System)剖析

make是一种自动化构建工具,通过读取Makefile将源代码自动编译为可执行程序和库文件。

虎哥LoveDroid 2168

nestJS学习

1、官方文档 第一步 | NestJS 中文文档 | NestJS 中文网 2、

zhizhuodewo6的博客 799

uniapp - H5网页打包运行报错Failed to load resource: net::ERR_FILE_NOT_FOUND(uniApp打包成H5部署到服务器,打包h5白屏并报错页面空白)

Failed to load resource: net::ERR_FILE_NOT_FOUND,uniapp 坑之打包之后白屏,uniapp打包h5,HBuilderX 打包H5后空白报错 Failed to load resource: net::ERR FILE NOT_FOUND,UNI-APP H5应用白屏(空白页)解决方案,白屏并报错Failed to load resource: net::ERR_FILE_NOT_FOUND,uniapp开发,打包成H5部署到服务器,uniapp打包之后首页

🏆 前端领域优质创作者 3578

我是如何学习 NestJS 的

NestJS企业级后端开发模板实践 本文分享了作者从前端转后端的学习历程和技术实践。作者选择NestJS作为后端入门框架,基于其TypeScript基础和清晰的架构设计,通过阅读文档和项目实践,最终构建了一个企业级后端开发模板。该模板采用六层模块化设计,包含认证授权、数据访问、共享服务等核心功能,集成了PostgreSQL、Redis等基础设施,并实现了RBAC鉴权、接口限流等安全机制。项目注重工程规范和开发体验,提供了完整的开发工具链和统一约定,支持快速创建和部署。文章最后给出了项目启动的具体步骤。

m0_58830399的博客 655

37、深入解析SCTP与RTP传输协议

本文深入解析了SCTP与RTP两种重要传输协议。SCTP提供可靠的多流数据传输,支持关联管理、选择性确认和错误处理,适用于高可靠性场景;RTP则专注于实时音视频数据的低延迟传输,配合RTCP实现质量监控。文章详细介绍了两种协议的数据结构、工作流程及应用场景,并对比其特性,帮助开发者根据可靠性、实时性和多流需求选择合适的协议。

time3的博客 83

【测开求职】面试题:操作系统 吐血整理

本篇文章是笔者为准备研究生复试和校招而写,因此主要针对问答,注重理解,而不在于细节计算。涵盖考研范围内的所有知识内容,读者可以先查看上面的链接走一遍操作系统的整体框架结构。其中,重点难点以及易混淆的知识点,笔者用其他文章进行了详述,在知识点处贴好了链接,有需要的可以查阅。另外操作系统在校招中有非常明显的重点,一般情况下只会考察进程管理,内存管理,和设备管理,且这三者考察频率依次递减,文件管理基本不会考察。

vector的博客 7908

NestJS Nest.js个人学习笔记

NestJS Nest.js个人学习笔记NestJS官网 NestJS官网 OpenAPI (Swagger) 相关 npm install --S @nestjs/swagger swagger-ui-express // pic.controller.ts import { ApiTags, ApiProperty, ApiOperation } from '@nestjs/swagger'; // 定义dto export class GetPicDto { @ApiProperty({

a1255215696的博客 859

YOLOv8改进 | 融合篇,YOLOv8主干改进融合网络之MobileNetV3+CA注意机制,助力涨点

MobileNetV3通过结合硬件感知网络架构搜索(NAS)和NetAdapt算法,通过新颖的架构改进进一步提升了性能。YOLOv8中的主干网络替换成MobileNetV3+引入CA注意机制

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 1881

nestjs学习(一)

nest学习(一) 核心文件目录 src -app.controller.ts -app.module.ts -main.ts 控制器controller import { Controller, Get, Res, Req, HttpStatus } from '@nestjs/common'; @Controller() export clas...

AndyViky的博客 1960

小众但实用:Python爬取钉钉/企业微信消息,实现办公数据自动化采集(完整版)

钉钉/企业微信的办公数据自动化采集,属于「小众但极度实用」的Python技巧——它不像爬虫爬取电商/网页数据那样被广泛关注,但能直接解决职场人的「刚需痛点」,把每天1-2小时的手动数据整理工作,变成「一键自动化完成」。这套技巧的核心价值,不在于「技术有多高深」,而在于「能真正落地、能真正提升效率」—— 几行简单的Python代码,就能解放双手,让你从繁琐的重复工作中解脱出来,专注于更有价值的核心工作。更重要的是:所有的技术和代码,都是基于「合规合法」的前提,守住了技术的底线,也守住了职场的原则。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1931

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

nestjs[入门与学习方向规划]

学习nestjs规划

墨水白云的博客 3049

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

NestJS 深入浅出

NestJS是一个基于TypeScript的Node.js框架,借鉴Spring框架的工程思想,旨在解决Node.js的工程失控问题。它通过模块化架构(Module)、依赖注入(IoC/DI)、分层请求拦截(Middleware/Guard/Pipe/Interceptor)等机制,实现代码的稳定性、可扩展性和可维护性。核心成员包括承载业务逻辑的Provider、处理请求的Controller,以及模块边界管理、生命周期钩子等特性。NestJS特别适合构建大型企业级应用,提供清晰的架构边界和标准化开发模式。

青蛙king的博客 1234

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

NestJs学习总结篇

原文地址 http://blog.poetries.top/2022/05/25/nest-summary Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用 JavaScript 的渐进增强的能力,使用并完全支持 TypeScript (仍然允许开发者使用纯 JavaScript 进行开发),并结合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。 在底层,Nest 构建在强大的 HTTP 服务器框架上,例.

上下求索 2052

NestJS学习记录-i18n国际化、Excel导出

HeaderResolver:['x-lang'],当前端调用时,在headers中添加key:x-lang,value:zh-CN就代表使用简体中文。QueryResolver:['lang'] ,前端调用时,传入lang=zh-CN,就表使用简体中文。在app.module.ts文件中,imports 中添加如上代码,动态导入I18nModule。最后创建一个excel.export模块,一个excel.service服务类。先创建两个Dto,一个用来做导出数据的类,一个用来做excle配置的类。

weixin_39179366的博客 1409

NestJS学习笔记

在npm或yorn的项目中都能找到,关于节点项目的一些元数据,使用nest-cli运行的脚本或依赖等,当我们使用npm或yorn安装一个包到项目时,就会被添加到dependencies中。在控制器类中有一些简单方法,用装饰器进行装饰,如@get、@post、@delete等,等代表他们的处理方法。通过装饰一个类来实现,该装饰器接受一个字符串,即是控制器的处理路径。如图所示,问题已经解决,但是,并没有原理图展示,因为没有指定原理图。负责处理传入的请求并将相应返回给客户端,会绑定一个特殊的路径。

weixin_51621120的博客 1790
上一篇: 尚硅谷kubernates学习笔记 1
下一篇: UOS获取ROOT权限
天落枫
博客等级 码龄10年 91粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值