如果你已经受够了 Express 的"自由散漫",又觉得 Next.js 全栈耦合太深,那么 NestJS 或许正是你一直在找的"后端全家桶"。
本文将从设计模式聊到实际代码,带你快速上手这个企业级 Node.js 框架。
📦 一、NestJS 是什么?和 Next.js 有什么不同?
| 框架 | 定位 | 特点 |
|---|---|---|
| Next.js | React 全栈框架 | 前端渲染 + 简易 API 路由,适合 BFF 层 |
| NestJS | Node.js 后端框架 | 纯后端,默认 TypeScript,全面模块化,适合企业级服务 |
✅ NestJS 默认使用 TypeScript ,并且大量借鉴了 Angular 的架构思想(模块、装饰器、依赖注入)。
✅ 它不仅支持 REST API ,还能做 GraphQL 、微服务 、WebSocket ,甚至可以对接 AI Infra 和底层系统集成。
🛠️ 二、快速安装与初始化
bash
bash
# 全局安装 NestJS CLI
npm i -g @nestjs/cli
# 创建新项目
nest new hello
# 进入目录并启动
cd hello
pnpm run start
访问 http://localhost:3000,你会看到熟悉的 "Hello World!"。
🏭 三、从"蜜雪冰城"理解工厂模式
工厂模式 是 23 种设计模式中最基础、最重要的一种。
它的核心思想是:你不需要关心对象怎么创建,只要告诉工厂"我要什么"就行。
举个🌰:蜜雪冰城的产品线
javascript
javascript
// 冰淇淋
class IceCream {
constructor() {
this.name = '冰淇淋';
this.price = 3;
}
show() {
console.log(`${this.name} 价格 ${this.price}`);
}
}
// 柠檬水
class LemonTea {
constructor() {
this.name = '柠檬水';
this.price = 4;
}
show() {
console.log(`${this.name} 价格 ${this.price}`);
}
}
// 珍珠奶茶
class MilkTea {
constructor() {
this.name = '珍珠奶茶';
this.price = 8;
}
show() {
console.log(`${this.name} 价格 ${this.price}`);
}
}
如果没有工厂模式,你每次都要 new IceCream()、new LemonTea(),一旦类名变化,所有调用处都要改 ------ 耦合度极高。
🏭 工厂类来解耦
javascript
typescript
class MixueFactory {
static create(type) {
switch (type) {
case 'ice': return new IceCream();
case 'lemon': return new LemonTea();
case 'milk': return new MilkTea();
default: throw new Error('未知产品');
}
}
}
// 使用工厂
const drink = MixueFactory.create('ice');
drink.show(); // 冰淇淋 价格 3
✅ 好处 :调用方只和
MixueFactory打交道,具体产品类的变化被隔离在工厂内部。✅ 对应到 NestJS 中,
NestFactory就是那个"蜜雪冰城",它负责创建应用实例。
typescript
javascript
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule); // 工厂创建 App
await app.listen(3000);
}
bootstrap();
🧱 四、模块化架构 ------ MVC 的优雅落地
NestJS 高度模块化,每个功能块都是一个 Module ,内部按 MVC 分层:
| 层级 | 文件 | 职责 |
|---|---|---|
| Controller | *.controller.ts |
接收请求,参数校验,调用 Service,返回响应 |
| Service | *.service.ts |
复杂业务逻辑、数据库操作(CRUD) |
| Module | *.module.ts |
组装 Controller + Service,并暴露给外部 |
📁 典型目录结构
text
ruby
src/
├── main.ts # 入口文件
├── app.module.ts # 根模块
├── app.controller.ts # 根控制器
├── app.service.ts # 根服务
└── todos/ # 独立业务模块
├── todos.module.ts
├── todos.controller.ts
└── todos.service.ts
🧩 五、装饰器模式 ------ 给类"贴标签"
装饰器模式:在不修改原有类代码的情况下,动态给类增加额外功能。
NestJS 中到处是装饰器:
@Module()------ 声明一个模块@Controller()------ 声明控制器@Injectable()------ 声明可被依赖注入的服务@Get()、@Post()------ 声明路由
typescript
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodosModule } from './todos/todos.module';
@Module({
imports: [TodosModule], // 引入外部模块
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
🧪 六、实战:Todos 模块 ------ 从 Controller 到 Service
1️⃣ 定义模块(组装层)
typescript
typescript
// todos.module.ts
import { Module } from '@nestjs/common';
import { TodosController } from './todos.controller';
import { TodosService } from './todos.service';
@Module({
controllers: [TodosController], // 注册控制器
providers: [TodosService], // 注册服务
})
export class TodosModule {}
2️⃣ 控制器(接收请求,返回响应)
typescript
less
// todos.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
} from '@nestjs/common';
import { TodosService } from './todos.service';
import { Todo } from './todos.service';
@Controller('todos') // 路由前缀 /todos
export class TodosController {
constructor(private readonly todosService: TodosService) {}
@Get(':id')
findOne(@Param('id') id: string): Todo {
console.log(`查询 id: ${id}`);
return this.todosService.findOne(Number(id));
}
@Post()
create(@Body('title') title: string): Todo {
return this.todosService.create(title);
}
@Delete(':id')
remove(@Param('id') id: string): { message: string } {
this.todosService.remove(Number(id));
return { message: '删除成功' };
}
@Put(':id')
update(
@Param('id') id: string,
@Body() patch: Partial<Todo>,
): Todo {
return this.todosService.update(Number(id), patch);
}
}
✅
@Param()和@Body()是参数装饰器,自动从请求中提取数据。✅ 控制器只做"调度",真正的数据操作交给 Service。
3️⃣ 服务层(真实业务逻辑 + 异常处理)
typescript
typescript
// todos.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
// 定义 Todo 类型
export interface Todo {
id: number;
title: string;
completed: boolean;
}
// 模拟数据库
let todos: Todo[] = [
{ id: 1, title: '学习 NestJS', completed: false },
{ id: 2, title: '学习 CRUD', completed: true },
];
let nextId = 3;
@Injectable()
export class TodosService {
// 查询所有
findAll(): Todo[] {
return todos;
}
// 查询单个
findOne(id: number): Todo {
const todo = todos.find((t) => t.id === id);
if (!todo) {
throw new NotFoundException(`Todo ${id} 不存在`);
}
return todo;
}
// 创建
create(title: string): Todo {
const newTodo: Todo = {
id: nextId++,
title,
completed: false,
};
todos.push(newTodo);
return newTodo;
}
// 删除
remove(id: number): void {
const index = todos.findIndex((t) => t.id === id);
if (index === -1) {
throw new NotFoundException(`Todo ${id} 不存在`);
}
todos.splice(index, 1);
}
// 更新
update(id: number, patch: Partial<Todo>): Todo {
const todo = this.findOne(id); // 复用 findOne 做校验
Object.assign(todo, patch);
return todo;
}
}
⚠️ 异常处理 :使用 NestJS 内置的
NotFoundException,会自动返回标准错误格式(状态码 + 消息)。✅
Partial<Todo>表示只传部分字段也可更新,非常灵活。
🧠 七、关于错误处理的思考
面试常问:你是如何处理后端报错的?
在 NestJS 中,我们不需要到处 try-catch,而是:
- 使用内置异常类(如
NotFoundException、BadRequestException)抛出错误。 - 框架自动捕获并返回标准 JSON:
json
json
{
"statusCode": 404,
"message": "Todo 99 不存在",
"error": "Not Found"
}
- 也可以自定义全局异常过滤器,统一错误格式。
✅ 八、总结
| 知识点 | 说明 |
|---|---|
| 工厂模式 | NestFactory.create() 帮你创建应用,解耦细节 |
| 模块化 | 每个业务独立成 Module,内部 MVC 分层 |
| 装饰器 | @Module、@Controller、@Injectable 等标记类职责 |
| 控制器 | 只负责参数提取和调用 Service,不写业务逻辑 |
| 服务 | 写真正的数据操作,支持依赖注入 |
| 异常处理 | 使用内置异常类,框架自动标准化输出 |
🎯 NestJS 不是玩具框架,它是能支撑起企业级后端架构的"重器"。
从工厂模式到装饰器,从模块化到依赖注入,每一处设计都体现着"可维护性"和"可扩展性"。
如果你正在寻找一个 结构清晰、TypeScript 友好、适合团队协作 的后端框架,NestJS 绝对值得一试。