
前端工程师极速入门 NestJS 后端 — 实战学习笔记含全套习题答案前言前端视角作为前端开发者学习 NestJS无需深究后端底层原理、操作系统、服务器内核等复杂知识核心目标是打通前后端开发链路独立开发可用、规范、符合企业标准的后端接口。前端入门 NestJS 只需要掌握核心 6 大能力基础 CRUD 接口编写精准接收前端各类请求参数统一接口响应格式适配前端 Axios 拦截器生产级跨域配置第三方数据库/工具全局挂载JWT 登录鉴权、接口权限控制本文基于实战踩坑经验总结贴合前端思维0 后端基础可直接上手学完可独立开发完整前后端项目。一、NestJS 核心架构前端必懂核心1. 核心分层架构企业固定规范NestJS 最核心的执行链路Controller控制器→ Service业务层Controller 控制器只负责「对接前端」接收请求、解析参数、调用业务服务不写任何业务逻辑Service 业务服务只负责「处理逻辑」数据校验、数据库操作、业务计算、数据处理不直接对接前端请求前端口诀必背控制器只传话服务层干实事职责分离不混乱。2. 依赖注入前端最易理解的核心特性依赖注入是 NestJS 的核心设计彻底告别手动实例化类解决前端开发者初学后端最大的困惑。核心代码示例typescriptconstructor(private readonly authService: AuthService) {}通俗白话解析无需手动new AuthService()实例化只需给 Service 类添加Injectable()装饰器Nest 框架自动全局创建单例实例全局唯一控制器直接通过this.authService.xxx()调用方法前端类比理解等同于全局挂载的公共工具类项目启动自动初始化任意页面/模块直接调用。二、控制器全套语法前端必会 100%1. Controller 路由前缀配置统一模块路由前缀简化接口路径是企业开发标准化写法。typescriptimport { Controller } from nestjs/common;Controller() // 无统一前缀接口需写完整路径Controller(products) // 统一前缀 /products当前模块所有接口自动拼接前缀企业规范99% 的业务模块必须配置统一路由前缀方便接口管理、版本迭代。2. 四大核心请求方式对应前端所有请求场景完全对齐前端 Axios 请求方法一一对应无学习成本Get()查询数据列表、详情、分页数据Post()新增数据、登录、提交表单Patch()局部更新数据只修改部分字段Delete()删除数据单条/批量删除3. 前端参数接收重中之重对接前端核心前端所有传参方式仅对应 4 种后端装饰器全覆盖无遗漏① 路径参数 Param动态路由 /:id适用场景获取单条详情、删除单条数据、修改单条数据typescriptimport { Get, Param, ParseIntPipe } from nestjs/common;Get(:id)findOne(Param(id, ParseIntPipe) id: number) {return this.productService.findOne(id);}核心关键点URL 路径参数默认都是字符串类型后端需要数字类型必须转换ParseIntPipe 两大核心作用自动字符串转数字、非法参数直接返回 400 错误拦截无效请求② 查询参数 QueryURL 拼接 ?keyvalue适用场景分页查询、条件筛选、关键词搜索、多参数筛选typescriptimport { Get, Query } from nestjs/common;import { QueryDto } from ./dto/query.dto;Get()findAll(Query() dto: QueryDto) {return this.productService.findAll(dto);}③ 请求体参数 BodyPost/Patch 表单参数适用场景新增数据、批量修改、复杂表单提交typescriptimport { Post, Body } from nestjs/common;import { CreateProductDto } from ./dto/create-product.dto;Post()create(Body() dto: CreateProductDto) {return this.productService.create(dto);}④ 完整请求对象 Request()适用场景获取请求头、登录用户信息、Cookie 等原生请求数据typescriptimport { Request } from nestjs/common;async login(Request() req) {return req.user; // 获取守卫解析后的登录用户信息}三、管道 Pipe 核心知识点参数校验神器ParseIntPipe前端开发高频使用是入门阶段最常用、必须掌握的内置管道专门处理数字类型参数。核心两大作用类型转换自动将 URL 字符串参数转为 Number 数字类型参数校验参数为空、字母、特殊符号等非法格式直接返回 400 客户端错误不会进入业务逻辑强制规范所有 ID、页码、偏移量等数字参数必须添加 ParseIntPipe 校验。四、全局拦截器统一返回格式适配前端1. 核心作用统一项目所有接口的响应结构解决后端接口返回格式不统一、前端适配繁琐的问题完美对接前端 Axios 全局拦截器。2. 标准统一响应结构json{code: 200,data: {}, // 真实业务数据message: success // 提示信息}3. 前端核心收益前端无需逐个适配接口返回格式只需配置一次 Axios 拦截器统一处理成功、失败状态大幅减少重复代码。五、跨域 CORS 原理生产级配置项目采用白名单动态跨域方案是企业生产环境标准写法区别于开发环境的全局放行。核心特性精准放行仅允许配置的前端域名、IP 跨域请求测试兼容Postman、Apifox 等无 Origin 的测试工具可直接放行安全拦截陌生域名、非法请求直接拦截杜绝跨域安全风险优劣对比app.enableCors()全局放行所有跨域请求仅适合本地开发线上生产环境禁止使用存在安全隐患。六、全局模块 Global 自定义依赖注入以 ClickHouse 数据库模块为例讲解第三方工具、数据库的全局挂载方案适配所有全局工具。1. Global() 全局模块装饰器被该装饰器标记的模块为全局模块全项目所有模块无需手动导入可直接注入使用。适用场景数据库连接、日志工具、缓存、全局常量、公共工具方法。2. 自定义注入令牌第三方库、原生实例无法通过类注入必须使用「字符串令牌 Inject」实现注入。typescript// 1. 定义注入令牌provide: CLICKHOUSE_CLIENT// 2. 全局注入使用import { Inject } from nestjs/common;constructor(Inject(CLICKHOUSE_CLIENT) private client) {}七、登录鉴权守卫AuthGuard守卫是 NestJS 权限控制核心先校验、后执行接口逻辑高效实现接口权限管控。1. 两种核心守卫全覆盖登录/权限场景AuthGuard(local)本地账号密码守卫仅用于登录接口校验用户名、密码是否正确AuthGuard(jwt)令牌校验守卫用于所有需要登录权限的接口校验 Token 有效性2. 守卫执行优先级守卫执行顺序请求进来 → 守卫校验 → 接口逻辑执行Token 缺失、过期、错误、无效直接返回 401 未授权错误不会进入业务代码。3. req.user 核心知识点req.user 不是 Node 原生对象是 JWT 守卫解析 Token 后自动挂载到请求对象上的用户信息可直接在接口中获取当前登录用户。八、前端开发者专属必背总结Controller 只负责接参、转发请求绝对不写业务逻辑Service 统一处理业务逻辑、数据操作、数据库查询所有数字类型参数ID、页码必须搭配 ParseIntPipe 校验全局工具/数据库采用 Global 全局模块 自定义令牌注入统一接口响应格式使用全局拦截器接口权限、登录校验使用守卫 AuthGuard生产环境必须使用白名单跨域禁止全局跨域放行九、企业级实战练习题前端转后端专用 完整标准答案所有练习题均来自真实项目开发场景完成后可直接写入简历适配中小企业全栈开发需求。练习1规范商品模块接口基础夯实需求给商品模块所有 Param 路径参数id添加 ParseIntPipe 类型校验依托全局响应拦截器统一所有 CRUD 接口返回格式配置接口权限新增/修改/删除接口需 JWT 鉴权查询接口公开访问标准答案typescriptimport { Controller, Get, Post, Patch, Delete, Param, Body, Query, ParseIntPipe, UseGuards } from nestjs/common;import { AuthGuard } from nestjs/jwt;import { ProductService } from ./product.service;import { CreateProductDto } from ./dto/create-product.dto;import { UpdateProductDto } from ./dto/update-product.dto;import { QueryProductDto } from ./dto/query-product.dto;Controller(products)export class ProductController {constructor(private readonly productService: ProductService) {}// 公开接口Get()findAll(Query() dto: QueryProductDto) {return this.productService.findAll(dto);}// 公开接口Get(:id)findOne(Param(id, ParseIntPipe) id: number) {return this.productService.findOne(id);}// 需登录鉴权Post()UseGuards(AuthGuard(jwt))create(Body() dto: CreateProductDto) {return this.productService.create(dto);}// 需登录鉴权Patch(:id)UseGuards(AuthGuard(jwt))update(Param(id, ParseIntPipe) id: number, Body() dto: UpdateProductDto) {return this.productService.update(id, dto);}// 需登录鉴权Delete(:id)UseGuards(AuthGuard(jwt))remove(Param(id, ParseIntPipe) id: number) {return this.productService.remove(id);}}练习2生产级跨域配置改造需求改造现有跨域配置支持前端携带 Token、Cookie 跨域请求配置多环境白名单本地开发、测试环境、线上生产环境域名标准答案main.tstypescriptimport { NestFactory } from nestjs/core;import { AppModule } from ./app.module;async function bootstrap() {const app await NestFactory.create(AppModule);// 多环境域名白名单const allowOriginList [http://localhost:5173,http://localhost:3000,https://test.xxx.com,https://www.xxx.com];app.enableCors({credentials: true, // 允许携带 Cookie、Tokenorigin: (origin, callback) {// 兼容 Postman/无Origin请求if (!origin || allowOriginList.includes(origin)) {callback(null, true);} else {callback(new Error(不允许跨域));}}});await app.listen(3000);}bootstrap();练习3封装全局日志模块高频面试考点需求模仿 ClickHouse 全局模块写法封装项目全局日志工具新建 LoggerModule 并设置为 Global 全局模块封装日志方法打印请求地址、请求方式、请求参数、请求时间在商品 CRUD 接口中调用日志方法记录每一次前端请求标准答案src/logger/logger.service.tstypescriptimport { Injectable } from nestjs/common;Injectable()export class LoggerService {requestLog(req: any) {const logInfo {url: req.url,method: req.method,query: req.query,body: req.body,time: new Date().toLocaleString()};console.log([请求日志], logInfo);}}src/logger/logger.module.ts全局模块typescriptimport { Global, Module } from nestjs/common;import { LoggerService } from ./logger.service;Global()Module({providers: [LoggerService],exports: [LoggerService]})export class LoggerModule {}在商品控制器使用typescriptconstructor(private readonly productService: ProductService,private readonly logger: LoggerService) {}Get()findAll(Query() dto: QueryProductDto, Request() req) {this.logger.requestLog(req);return this.productService.findAll(dto);}练习4精细化接口鉴权真实项目权限逻辑需求实现差异化权限控制贴合后台管理系统真实场景登录接口、注册接口无需 Token公开访问商品列表、商品详情查询接口无需 Token公开访问商品新增、编辑、删除接口必须携带有效 JWT Token未登录直接拦截标准答案最终权限规则直接复用【练习1】代码即可实现登录、注册、商品查询无UseGuards公开访问新增/修改/删除添加UseGuards(AuthGuard(jwt))强制鉴权练习5全局统一异常处理项目必备优化需求配合现有响应拦截器完善项目异常处理机制创建全局异常过滤器捕获项目所有未知错误、业务错误统一错误返回格式code500、message错误描述、datanull区分参数错误、权限错误、服务器错误返回对应提示标准答案全局异常过滤器 src/filter/http-exception.filter.tstypescriptimport { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from nestjs/common;Catch()export class HttpExceptionFilter implements ExceptionFilter {catch(exception: unknown, host: ArgumentsHost) {const ctx host.switchToHttp();const response ctx.getResponse();let code 500;let message 服务器异常;if (exception instanceof HttpException) {const status exception.getStatus();const errRes exception.getResponse() as any;code status;message Array.isArray(errRes.message) ? errRes.message[0] : errRes.message;}response.status(200).json({code,message,data: null});}}main.ts 全局注册typescriptimport { HttpExceptionFilter } from ./filter/http-exception.filter;app.useGlobalFilters(new HttpExceptionFilter());练习6前端适配型分页接口Table 组件专用需求改造商品列表接口适配 Ant Design/Element Plus 表格组件分页需求接收前端传入的 page当前页码、pageSize每页条数参数后端实现分页查询逻辑统计数据总数固定返回结构{ page, pageSize, total, list }标准答案分页 DTOquery-product.dto.tstypescriptimport { IsOptional, IsInt, Min } from class-validator;import { Transform } from class-transformer;export class QueryProductDto {IsOptional()Transform(v Number(v.value))IsInt()Min(1)page: number 1;IsOptional()Transform(v Number(v.value))IsInt()Min(1)pageSize: number 10;}Service 分页逻辑typescriptasync findAll(dto: QueryProductDto) {const { page, pageSize } dto;// 模拟数据库分页真实项目替换为 ORM 分页const total 86;const list [];return {page,pageSize,total,list};}