系统学习Nest.js基础

28/07/2026

系统学习Nest.js基础

学习Nest.js的基本语法、结构,如何编写接口。

#nest.js

今天学习Nest.js的基础,Nest.js 是一个用于构建企业级 Node.js 后端应用的框架,这个架构借鉴了 Angular 的架构思想,底层运行在 Express 或 Fastify 之上。

学习前的思考:

1. 为什么使用Express而不用Koa呢?

Nest 诞生时 Express 是 Node.js 最大生态,企业应用大量依赖 Express,而 Nest 的目标是给 Express 增加企业级架构能力。

2. 为什么还要支持Fastify呢?

Express在大型项目会存在性能问题,而Fastify在高并发场景的性能更好。 而最重要的Nest 想解决的是“应用架构问题”,而不是“HTTP 底层问题。所以最初底层设计就有一个适配器:Express适配器,后面Fastify出来,也对Fastify进行适配。 Nest.js采用的可拔插的架构。

常用命令

创建项目

nest new <name>

生成类的 generate(nest g)

nest g xx ...

完整名 简写 说明
module mo 模块(@Module)
controller co 控制器(路由/接口)
service s 服务(业务逻辑,@Injectable)
resource res CRUD 一整套(module + controller + service + dto 等)
guard gu 守卫(鉴权、权限拦截)
interceptor itc 拦截器(请求/响应前后处理)
pipe pi 管道(参数校验/转换)
filter f 异常过滤器
middleware mi 中间件
decorator d 自定义装饰器
gateway ga WebSocket 网关
provider pr 普通 Provider
class cl 普通类
interface i TypeScript interface
dto DTO(数据传输对象)
exception 自定义异常类

基础知识

导出模块:export

全局模块共享:Global

当有一个模块是存在通用的,任意模块能使用,则使用global。 @Global:全局模块,可以在其他模块中注入 这时还需要配合将此模块导出:exports


@Global() // 全局模块,可以在其他模块中注入
@Module({
  providers: [
      {
        provide: 'Config',
        useValue: {
          baseUrl: '/api',
        },
      },
    ],

    exports: [
      {
        provide: 'Config',
        useValue: {
          baseUrl: '/api',
        },
      },
    ],
})
export class ConfigModule {}



@Controller('list')
export class ListController {
  constructor(
    private readonly listService: ListService,
    
    @Inject('Config') private readonly config: any,
  ) {}

  @Get()
  findAll() {
    return this.config.baseUrl;
  }
}

动态模块


import { DynamicModule, Global, Module } from '@nestjs/common';

interface Options {
  path: string;
}

@Global() // 全局模块,可以在其他模块中注入
@Module({ })
export class ConfigModule {
  // 动态模块,可以动态注入配置
  static forRoot(options: Options): DynamicModule {
    return {
      module: ConfigModule,
      providers: [
        {
          provide: 'Config',
          useValue: {
            baseUrl: '/api' + options.path,
          },
        }, 
      ],
      exports: [
        {
          provide: 'Config',
          useValue: {
            baseUrl: '/api' + options.path,
          },
        },
      ],
    };
  }
}



import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ListModule } from './list/list.module';
import { ConfigModule } from './config/config.module';
 
@Module({
  imports: [
    ListModule,
    // 动态注入配置
    ConfigModule.forRoot({ path: 'yanghesu' }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

中间件

命令: nest g mi xxx。创建中间件模块

应用到具体路由

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('我来了~~~');
    next();
  }
}



import {
  Module,
  NestModule,
  MiddlewareConsumer,
  RequestMethod,
} from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

import { Logger } from '../middleware';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule implements NestModule {
  // 配置中间件
  configure(consumer: MiddlewareConsumer) {
    // 三种方式应用中间件
    // 1.应用中间件
    // consumer.apply(Logger).forRoutes('user');
    // 2.应用到整个控制器
    // consumer.apply(Logger).forRoutes(UserController);
    // 3.应用到单个路由
    consumer
      .apply(Logger)
      // 只对get请求应用中间件
      .forRoutes({ path: 'user', method: RequestMethod.GET });
  }
}
全局中间件


import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Request, Response, NextFunction } from 'express';
import cors from 'cors';

// 全局中间件
function MiddlewareAll(req: Request, res: Response, next: NextFunction) {
  console.log('我是全局中间件===', req.originalUrl);
  next();
}

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 使用cors中间件-允许跨域
  app.use(cors());
  // 使用全局中间件
  app.use(MiddlewareAll);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

静态文件上传:multer

搭配安装:@types/multer 搭配使用(内置):@nestjs/platform-express


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

@Module({
  imports: [
    // 注册multer模块, 
   // register同步注册,固定路径
  //  registerAsync异步注册,适用依赖环境变量、配置中心、数据库等运行时才能拿到的值。
    MulterModule.register({
      // 配置存放路径
      storage: diskStorage({
        // 配置 文件存放路径目录
        destination: join(__dirname, '../image'),
        // 配置文件名
        filename: (req, file, cb) =&gt; {
          const fileName = `${Date.now() + extname(file.originalname)}`;
          return cb(null, fileName);
        },
      }),
    }),
  ],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule {}


import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { CreateUploadDto } from './dto/create-upload.dto';
import { UpdateUploadDto } from './dto/update-upload.dto';
// FileInterceptor 单个文件
// FilesInterceptor 多个文件
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('album')
  // 处理文件上传
 // 解析并保存文件
  @UseInterceptors(FileInterceptor('file'))
 // @UploadedFile() 把文件对象塞进file
  upload(@UploadedFile() file) {
    console.log('file===', file);
    return '上传成功了';
  }
}



// upload.module.ts
import { Module } from '@nestjs/common';
import { MulterModule } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';
import { UploadController } from './upload.controller';
import { UploadService } from './upload.service';

@Module({
  imports: [
    MulterModule.register({
      storage: memoryStorage(), // 先不落盘,方便处理
    }),
  ],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule {}


// 进行文件校验
import {
  Controller,
  Post,
  UploadedFile,
  UseInterceptors,
  ParseFilePipe,
  MaxFileSizeValidator,
  FileTypeValidator,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { UploadService } from './upload.service';

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('image')
  @UseInterceptors(FileInterceptor('file'))
  uploadImage(
    @UploadedFile(
     // Nest 内置的“文件校验管道”
    // // 在进入方法体之前先跑 validators;任一失败会直接抛异常,不会继续执行后面的业务代码
      new ParseFilePipe({ 
        // validators:校验器列表,按顺序执行
        validators: [
          new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 }), // 5MB
          new FileTypeValidator({ fileType: /(jpg|jpeg|png|webp)$/ }), // 用正则匹配允许的类型
        ],
      }),
    )
    file: Express.Multer.File,
  ) {
    return this.uploadService.saveImage(file);
  }
}


// 压缩转格式后再存
import { Injectable } from '@nestjs/common';
import { join } from 'path';
import { writeFile, mkdir } from 'fs/promises';
import sharp from 'sharp';
@Injectable()
export class UploadService {
  async saveImage(file: Express.Multer.File) {
    const dir = join(process.cwd(), 'uploads', 'images');
    await mkdir(dir, { recursive: true });
    const filename = `${Date.now()}.webp`;
    const filepath = join(dir, filename);
    // 图片处理:压缩 + 转 webp
    const buffer = await sharp(file.buffer)
      .rotate()          // 按 EXIF 纠正方向
      .resize({ width: 1920, withoutEnlargement: true })
      .webp({ quality: 80 })
      .toBuffer();
    await writeFile(filepath, buffer);
    // 生产环境这里通常改为:await oss.put(filename, buffer)
    return {
      url: `/static/images/${filename}`,
      size: buffer.length,
    };
  }
}


下载文件

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  UseInterceptors,
  UploadedFile,
  Res,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { CreateUploadDto } from './dto/create-upload.dto';
import { UpdateUploadDto } from './dto/update-upload.dto';
// FileInterceptor 单个文件
// FilesInterceptor 多个文件
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { join } from 'path';
import type { Response } from 'express';
import { zip } from 'compressing';

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  // 1. 直接下载文件
  @Get('export')
  download(@Res() res: Response) {
    const filePath = join(__dirname, '../image/1785242445615.jpeg');
    res.download(filePath);
  }

  // 2. 下载文件 - 流的方式(压缩后再下载)
  @Get('stream')
  async down(@Res() res: Response) {
    // 拼接本地文件绝对路径(编译后 __dirname 为 dist/upload,../image 指向 dist/image)
    const filePath = join(__dirname, '../image/1785242445615.jpeg');
    // 创建一个 zip 压缩流(边压缩边输出,不先写完整 zip 到磁盘)
    const tarStream = new zip.Stream();
    // 把指定文件加入压缩流(可多次调用以打包多个文件)
    await tarStream.addEntry(filePath);
    // 告诉浏览器:响应体是二进制流(通用下载类型)
    res.setHeader('Content-Type', 'application/octet-stream');
    // 告诉浏览器:以附件形式下载,并指定默认保存的文件名
    res.setHeader(
      'Content-Disposition',
      'attachment; filename="1785242445615.jpeg"',
    );
    // 把压缩流管道接到 HTTP 响应,数据会持续推给客户端
    tarStream.pipe(res);
  }

}

全局响应拦截


import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

// 统一响应体里 data 字段的类型(T 为业务数据的实际类型)
interface Data&lt;T&gt; {
  data: T;
}

// 全局/局部响应拦截器:把控制器返回值包装成统一 JSON 结构
@Injectable()
export class GlobalResponse&lt;T&gt; implements NestInterceptor {
  // Nest 在请求进入控制器前后会调用此方法
  intercept(
    context: ExecutionContext, // 当前请求上下文(可取 req/res、handler 等信息)
    next: CallHandler&lt;any&gt;, // 调用链:用来继续执行后续拦截器/控制器
  ): Observable&lt;Data&lt;T&gt;&gt; {
    // next.handle():执行控制器方法,得到原始返回值的 Observable
    return next.handle().pipe(
      // map:在响应流出前,把原始 data 改造成统一格式
      map((data) =&gt; {
        return {
          data, // 业务数据(控制器 return 的内容)
          status: 0, // 业务状态码(0 表示成功,可按项目约定调整)
          success: true, // 是否成功
          message: 'success', // 提示信息
        };
      }),
    );
  }
}



import { GlobalResponse } from './common/response';
// 使用全局拦截器
  app.useGlobalInterceptors(new GlobalResponse());