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