吃透 NestJS 核心|工厂模式 + 依赖注入 + Todo CRUD 实战,后端入门到上手

前言

写 Node 后端很多人从 Express 上手,写小项目很爽,一旦业务膨胀,路由、业务逻辑、数据操作全部堆在一起,代码杂乱难维护,不好做单元测试。

NestJS 是一套基于 TypeScript 的企业级 Node 后端框架,把模块化、MVC 分层、依赖注入、装饰器、经典设计模式全部融入框架本身,非常适合开发 API 服务、微服务、AI 底层服务。

划重点:不要混淆!

  • NestJS:Node 后端框架(本文)
  • NextJS:React 全栈框架,二者毫无关系

后端开发主要做什么

  1. 对外提供 RESTful API,对接前端做 Web 开发
  2. 系统集成、并发处理、底层服务、AI Infra 基建
  3. 业务拆分,搭建微服务架构

环境安装与项目启动

bash

运行

bash 复制代码
# 全局安装nest脚手架
npm i -g @nestjs/cli
# 创建新项目
nest new hello
# 启动开发环境
nest start

初始核心目录

  • src/main.ts:项目入口文件,程序启动从这里开始
  • app.module.ts:根模块,整个应用的组装调度中心

理解工厂模式:看懂 NestFactory

工厂模式是 23 种设计模式中最重要的模式之一,核心思想:面向接口编程,把对象创建和业务使用解耦,使用者不用关心实例化细节

通俗比喻:想喝奶茶不用自己买原料熬煮,直接找蜜雪冰城(工厂)下单,工厂帮你生产好饮品,你直接使用。Nest 中的 NestFactory 就是这个 "蜜雪冰城工厂",我们不需要手动 new 一大堆底层组件,调用NestFactory.create()就拿到应用实例。

蜜雪冰城工厂模式示例代码

javascript

运行

typescript 复制代码
// 产品1:冰淇淋
class IceCream {
  constructor() {
    this.name = '冰激凌';
    this.price = 3;
  }
  // 统一接口,所有产品都实现show方法
  show() {
    console.log(`${this.name},${this.price}`);
  }
}

// 产品2:柠檬水
class LemonTea {
  constructor() {
    this.name = '柠檬水';
    this.price = 4;
  }
  show() {
    console.log(`${this.name},${this.price}`);
  }
}

// 产品3:珍珠奶茶
class MilkTea {
  constructor() {
    this.name = '珍珠奶茶';
    this.price = 8;
  }
  show() {
    console.log(`${this.name},${this.price}`);
  }
}

// ✅ 工厂类:统一生产实例,使用者只和工厂打交道
class MiXueFactory {
  static create(type) {
    switch (type) {
      case 'ice':
        return new IceCream();
      case 'tea':
        return new LemonTea();
      case 'milk':
        return new MilkTea();
    }
  }
}

// 使用方,不需要手动new各个产品
const drink1 = MiXueFactory.create('ice');
drink1.show();
const drink2 = MiXueFactory.create('tea');
drink2.show();
const drink3 = MiXueFactory.create('milk');
drink3.show();

好处:新增饮品,只需要新增产品类,修改工厂,调用方代码完全不用改动,实现解耦。

NestJS 高度模块化,MVC 分层

Nest 有强约定,一个业务模块固定三件套:

  1. xx.module.ts:模块组装层,注册 controller、provider
  2. xx.controller.ts:控制器,接收 http 请求,参数处理,返回响应,禁止写复杂业务
  3. xx.service.ts:服务层 provider,处理核心业务逻辑、数据操作

两个核心概念

  1. @Injectable() :装饰器,把类交给 Nest 容器管理,开启依赖注入,不需要手动new,构造函数直接注入使用。
  2. 装饰器模式 :不修改原有类内部代码,通过@符号动态给类、方法增加能力,比如绑定路由前缀、请求方法。

铁律:Controller 只负责接收转发请求,业务逻辑、数据操作全部下沉 Service 层。

Nest 异常处理(面试高频)

面试题:NestJS 中如何处理后端报错?

原生 JS 我们会用try catch捕获异常,而 Nest 内置大量标准化异常类,例如NotFoundException。直接抛出框架异常,框架自动包装统一格式返回,自动带上statusCodemessage,前端解析更方便,后续还可以配合全局异常过滤器统一处理。

Todo CRUD 完整实战

实现待办事项增删改查,内存数组模拟数据库,企业项目替换成 ORM 即可。

todos.service.ts 业务服务层

typescript

运行

typescript 复制代码
import { Injectable, NotFoundException } from '@nestjs/common';

// Todo数据类型定义
export interface Todo {
  id: number;
  title: string;
  complete: boolean;
}

// 模拟数据库,真实项目替换数据库
const todos: Todo[] = [
  { id: 1, title: '学习 NestJS', complete: false },
  { id: 2, title: '学习 CRUD', complete: true },
];
let nextId = 3;

// ✅ @Injectable 交给Nest容器管理,支持依赖注入
@Injectable()
export class TodosService {
  // 查询全部待办
  findAll(): Todo[] {
    return todos;
  }

  // 根据id查询单条
  findOne(id: number): Todo {
    const todo = todos.find((t) => t.id === id);
    // 业务容错,数据不存在抛出404标准异常
    if (!todo) throw new NotFoundException(`Todo ${id} 不存在`);
    return todo;
  }

  // 新增待办
  create(title: string): Todo {
    const todo: Todo = { id: nextId++, title, complete: false };
    todos.push(todo);
    return todo;
  }

  // 删除待办
  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);
    Object.assign(todo, patch);
    return todo;
  }
}

todos.controller.ts 控制器层

typescript

运行

less 复制代码
import { Controller, Get, Post, Param, Body, Delete, Put } from '@nestjs/common';
import { TodosService } from './todos.service';
import { type Todo } from './todos.service';

// ✅ 路由前缀,所有接口前缀 /todos
@Controller('todos')
export class TodosController {
  // ✅ 依赖注入,nest自动实例化service,不用手动new
  constructor(private readonly todosService: TodosService) {}

  // GET /todos 获取全部
  @Get()
  findAll(): Todo[] {
    console.log('/todos controller');
    return this.todosService.findAll();
  }

  // GET /todos/:id 获取单条
  @Get(':id')
  findOne(@Param('id') id: string): Todo {
    console.log(id);
    return this.todosService.findOne(Number(id));
  }

  // POST /todos 创建待办
  @Post()
  create(@Body('title') title: string): Todo {
    return this.todosService.create(title);
  }

  // DELETE /todos/:id 删除待办
  @Delete(':id')
  remove(@Param('id') id: string): { message: string } {
    this.todosService.remove(Number(id));
    return { message: '删除成功' };
  }

  // PUT /todos/:id 更新待办
  @Put(':id')
  update(@Param('id') id: string, @Body() patch: Partial<Todo>): Todo {
    return this.todosService.update(Number(id), patch);
  }
}

todos.module.ts 模块组装

typescript

运行

typescript 复制代码
import { Module } from '@nestjs/common';
import { TodosController } from './todos.controller';
import { TodosService } from './todos.service';

// 注册本模块控制器和服务提供者
@Module({
  controllers: [TodosController],
  providers: [TodosService],
})
export class TodosModule {}

⚠️ 注意:需要把TodosModule导入根模块AppModuleimports数组中,接口才会生效。

核心要点总结

  1. NestJS 基于 TS,主打模块化、依赖注入、装饰器,用于企业级 Node 后端开发。
  2. 分层原则:Controller 负责接收 http 请求,Service 实现业务逻辑,职责隔离。
  3. 工厂模式:对象创建与业务逻辑解耦,NestFactory就是典型实现。
  4. 异常处理优先使用框架内置异常类,统一接口返回格式。
  5. @Injectable()+ 构造函数注入,省去手动实例化,便于单元测试。
相关推荐
Asize2 小时前
3 种设计模式 + DI:Nest.js 后端第一课
后端·设计模式·nestjs
moMo18 小时前
深入 NestJS:从工厂模式到装饰器模式的架构之美
typescript·nestjs
HjhIron1 天前
NestJS 入门指南:从工厂模式到模块化 CRUD 实战
前端·nestjs
凌涘1 天前
NestJS 的模块化后端组织方式
nestjs
小林ixn1 天前
NestJS 入门实战:从 0 到 1 撸一个 Todo CRUD,感受装饰器与模块化的优雅
后端·mvc·nestjs
烬羽1 天前
NestJS 依赖注入:Controller 里那个没有 new 的 service,到底从哪来的?
设计模式·typescript·nestjs
渣波1 天前
NestJS 企业级后端架构实战:从核心代码到工程化思维的深度重构
前端·typescript·nestjs
不好听6131 天前
NestJS 是什么?一张图看懂企业级后端的骨架
后端·nestjs
嘟嘟07171 天前
NestJS 入门:从 NestFactory 入口到 Module/Controller/Service 模块化结构一次讲清
typescript·node.js·nestjs