前言
说起 Node.js 后端开发,很多人从 Express、Koa 这类轻量框架转向 NestJS 时,最不适应的就是满眼的 @ 装饰器、模块化拆分和依赖注入。
NestJS 是 Node 运行环境下的企业级后端开发框架,默认原生支持 TypeScript,全面贯彻模块化设计思想,非常适合构建中大型 Web 服务、系统集成服务乃至微服务架构。
本文就从基础定位、目录结构到核心组件,再到底层设计模式,把 NestJS 入门最核心的知识点一次性串清楚。
一、后端开发到底在做什么?
在聊框架之前,先明确后端开发的核心工作,大致分为三类:
- 提供 API 接口:最常见的 Web 开发场景,给前端、客户端提供 HTTP 接口,完成数据增删改查。
- 系统集成与底层服务:处理并发、对接第三方服务、封装底层能力、做任务调度等。
- 微服务架构:拆分业务域,构建分布式微服务集群,支撑复杂业务系统。
NestJS 的设计目标,就是用标准化的模块化架构,优雅地支撑以上所有场景。
二、快速安装与项目初始化
NestJS 提供了官方 CLI 工具,用来快速创建项目、生成模块 / 控制器等代码片段。
1. 全局安装 CLI
css
npm i -g @nestjs/cli
-g代表全局安装,安装完成后,在电脑任意目录都可以使用nest命令,只需要安装一次。
2. 创建项目
在你想存放代码的目录下执行:
arduino
nest new hello-nest
执行过程中会提示选择包管理器、是否开启可观测性、选择模块系统,入门阶段建议选择:
- 包管理器:npm /pnpm 均可
- 可观测性:No(初学不需要监控链路)
- 模块系统:ESM(ES Modules,现代标准)
三、核心设计:高度模块化的架构思想
NestJS 最核心的特征就是模块化。整个应用由一个个模块拼接而成,每个模块职责单一,各自管理自己的控制器、服务和依赖。
基础目录结构
ruby
src
├── main.ts # 项目入口,启动应用
├── app.module.ts # 根模块,整个应用的入口模块
├── app.controller.ts # 根控制器
└── app.service.ts # 根服务
模块化层级
markdown
整个应用 App
└── 根模块 AppModule
├── 导入其他子模块
├── 注册控制器 Controller
└── 注册服务 Provider / Service
每个模块都是一个独立的单元,通过 @Module 装饰器声明自己的依赖和组件,模块之间可以互相导入、复用。
四、核心三件套:Module / Controller / Service
NestJS 代码里最常见的三个装饰器,分别对应三层职责,分层清晰是企业级框架的典型特征。
1. @Module:模块组装器
@Module() 打在类上,标记这个类是一个 Nest 模块,作用是组装和注册:告诉框架这个模块包含哪些控制器、哪些服务,以及需要导入哪些其他模块。
typescript
import { Module } from '@nestjs/common';
import { AppController } from './app.controller.js';
import { AppService } from './app.service.js';
@Module({
imports: [], // 导入其他依赖模块
controllers: [AppController], // 注册当前模块的控制器
providers: [AppService], // 注册当前模块的服务(提供者)
})
export class AppModule {}
imports:引入其他模块,比如用户模块、数据库模块controllers:注册控制器,只有写在这里的控制器,它的路由才会生效providers:注册服务,注册后 Nest 的依赖注入才能自动创建实例
2. @Controller:请求接收器
@Controller() 标记一个类为控制器,它是 HTTP 请求的入口,负责接收请求、校验参数、调用业务层、返回响应结果。
简单说:控制器负责 "接请求、调服务、返结果",不写复杂业务逻辑。
typescript
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service.js';
@Controller()
export class AppController {
// 构造函数注入服务
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
console.log('/的控制器');
// 业务逻辑全部交给 service 层
return this.appService.getHello();
}
}
@Controller()可以传入路由前缀,比如@Controller('user'),该控制器下所有接口都会加上/user前缀@Get()标记方法处理 GET 请求,同理还有@Post()、@Put()、@Delete()
3. @Service:业务逻辑层
Service 是真正写业务逻辑的地方:数据计算、数据库操作、第三方调用都写在这里。
控制器层尽量保持轻薄,业务逻辑下沉到 Service,好处是逻辑可复用、易测试、职责清晰。
五、底层原理:语法糖与设计模式
看懂了用法,再深入一层:NestJS 这些简洁的写法背后,是 TypeScript 语法特性和经典设计模式的支撑。
1. TS 构造函数参数修饰符
很多人刚接触会疑惑:为什么构造函数里写 private readonly appService,就能用 this.appService 访问?
这是 TypeScript 的专属语法糖:当构造函数参数加上 private / public / protected 修饰符时,TS 编译器会自动完成两件事:
- 自动给类声明一个同名的实例属性
- 构造函数内部自动执行
this.xxx = xxx赋值
typescript
// 语法糖写法
constructor(private readonly appService: AppService) {}
等价于完整写法:
typescript
private readonly appService: AppService;
constructor(appService: AppService) {
this.appService = appService;
}
注意:如果不加修饰符,它就只是构造函数的局部参数,出了构造函数就无法通过
this访问。
2. 装饰器模式
代码里到处都是的 @Module、@Controller、@Get,并不是注释,也不只是为了好看,它们是装饰器语法,底层思想来自经典的「装饰器设计模式」。
装饰器模式的核心:不修改原有类的代码,动态给对象扩展额外功能,包装原有对象,在前后增加逻辑。
NestJS 中的装饰器本质是函数,它会给类、方法打上元数据标签。框架启动时扫描这些标签,自动完成路由注册、模块组装、依赖注入。
简单理解:装饰器就是给类 / 方法贴电子标签,框架能读懂标签并按标签工作。删掉装饰器,对应的功能就会直接失效。
3. 依赖注入(DI)
你不需要自己 new AppService(),只要在构造函数里声明类型,Nest 就会自动把实例注入进来。
这就是依赖注入:对象的创建和销毁由框架统一管理,开发者只需要声明依赖,不需要手动实例化。 好处是解耦、便于测试、便于统一管理实例生命周期。
六、总结
最后用一句话把 NestJS 入门核心串起来:
NestJS 以模块化为骨架,用
@Module组装组件,用@Controller接收请求,用@Service处理业务,依托 TypeScript 语法糖和装饰器模式,实现了优雅的依赖注入,构建出高内聚、低耦合的企业级后端架构。
掌握这些核心概念,再去写接口、拆模块、接数据库,思路就会清晰很多。后续还可以继续深入中间件、拦截器、管道、数据库集成等进阶内容。