第一个应用
本教程共 47 篇 · 第 3 篇 · 更新于 2026-08-09 · 约 10 分钟阅读
本节目标:拆解脚手架生成的代码,搞清楚每个文件在干什么,然后自己加路由、加模块。
入口文件 main.ts
每个 NestJS 应用都有一个入口文件 src/main.ts。它做的事情很简单——启动 HTTP 服务。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
这段代码做了两件事:
NestFactory.create(AppModule)—— 用根模块创建一个 NestJS 应用实例app.listen(3000)—— 在 3000 端口启动 HTTP 服务
Note
bootstrap()函数名是约定俗成的叫法,你也可以叫别的名字。但大家都叫bootstrap,就别特立独行了。
NestFactory 是 NestJS 提供的工厂类,负责创建应用实例。它返回的 app 对象实现了 INestApplication 接口,提供了一系列方法,比如设置全局前缀、启用 CORS 等等。后面会用到。
根模块 app.module.ts
NestJS 是模块化的框架,整个应用从根模块开始组装。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
@Module() 装饰器接收一个配置对象,有四个属性:
| 属性 | 作用 |
|---|---|
imports | 导入其他模块(依赖) |
controllers | 注册本模块的控制器 |
providers | 注册本模块的服务(提供者) |
exports | 导出服务,让别的模块能用 |
你可以把模块想象成一个”功能包”。AppModule 是根模块,相当于整个应用的”总装配图”——它告诉 NestJS:“我这个应用有这些控制器、这些服务、依赖了这些模块。”
Tip现在
imports是空的,后面加功能模块时会往里面填。
控制器 app.controller.ts
控制器负责接收 HTTP 请求,然后返回响应。
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
几个关键点:
@Controller()标记这是一个控制器。不传参数就是根路径/@Get()标记这个方法处理 GET 请求- 构造函数里注入了
AppService,调用它的getHello()方法
这里有个你可能注意到的细节:private readonly。这是 TypeScript 的语法糖,一行代码同时完成了”声明属性 + 赋值 + 只读”三件事。
服务 app.service.ts
服务是真正干活的地方,业务逻辑都写在这里。
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
@Injectable() 装饰器告诉 NestJS:“这个类可以被依赖注入系统管理。”
你可能会问:为什么不直接在控制器里写业务逻辑?
因为职责分离。控制器只负责”接请求、回响应”,服务负责”干活”。这样以后逻辑变了,只改服务就行,控制器不用动。而且服务可以被多个控制器复用。
Note你可能会觉得现在这个项目很简单,分不分无所谓。但养成好习惯很重要——等代码多了再想拆分就晚了。
启动流程
整个应用的启动流程可以简化成这样:
1. 执行 main.ts 中的 bootstrap()
↓
2. NestFactory.create(AppModule)
- 加载根模块
- 解析模块依赖树
- 初始化依赖注入容器
- 实例化所有 Provider
↓
3. app.listen(3000)
- 创建 HTTP 服务器
- 绑定端口
- 开始监听请求
↓
4. 应用就绪,等待请求
你不需要记住每个细节。知道”main.ts 启动 → 加载模块 → 启动服务”这个大流程就够了。
加几个路由试试
光看代码不够直观,来动手改改。
在 app.controller.ts 里加两个路由:
import { Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('welcome')
getWelcome(): string {
return 'Welcome to NestJS!';
}
@Post('create')
create(): string {
return 'Resource created!';
}
}
启动项目后:
- 访问
GET http://localhost:3000/→ 返回 “Hello World!” - 访问
GET http://localhost:3000/welcome→ 返回 “Welcome to NestJS!” - 发送
POST http://localhost:3000/create→ 返回 “Resource created!”
路由装饰器一览:
| 装饰器 | HTTP 方法 | 用途 |
|---|---|---|
@Get() | GET | 获取资源 |
@Post() | POST | 创建资源 |
@Put() | PUT | 完整更新资源 |
@Patch() | PATCH | 部分更新资源 |
@Delete() | DELETE | 删除资源 |
@All() | 所有方法 | 匹配所有 HTTP 方法 |
用 CLI 创建一个用户模块
来点实际的。用 CLI 生成一个用户模块,包含控制器和服务:
nest generate module users
nest generate controller users
nest generate service users
三条命令可以合成一条:
nest g resource users
不过 nest g resource 会生成更多文件(包括 DTO、Entity),后面讲到再讲。现在先用手动的方式一步步来。
执行完后,src/ 下会多一个 users/ 目录:
src/
├── users/
│ ├── users.module.ts
│ ├── users.controller.ts
│ └── users.service.ts
├── app.module.ts
├── app.controller.ts
├── app.service.ts
└── main.ts
写用户服务
// users/users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find(user => user.id === id);
}
create(createUserDto: { name: string; email: string }) {
const newUser = {
id: this.users.length + 1,
...createUserDto,
};
this.users.push(newUser);
return newUser;
}
}
这里用了内存数组模拟数据库。数据存在内存里,重启就没了。后面会接真正的数据库。
写用户控制器
// users/users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Post()
create(@Body() createUserDto: { name: string; email: string }) {
return this.usersService.create(createUserDto);
}
}
@Controller('users') 设置了路由前缀为 /users。所以:
GET /users→ 调用findAll()GET /users/1→ 调用findOne(),:id是路径参数POST /users→ 调用create(),@Body()提取请求体
注册到根模块
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
注意 imports: [UsersModule] 这一行。根模块导入了用户模块,NestJS 才知道有这个模块存在。
Tip用
nest g module users生成模块时,CLI 会自动把UsersModule加到AppModule的imports里。手动创建的话别忘了这一步。
测试 API
启动项目,用 curl 测试:
# 获取所有用户
curl http://localhost:3000/users
# 获取单个用户
curl http://localhost:3000/users/1
# 创建用户
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"王五","email":"wangwu@example.com"}'
你也可以用 Postman、Insomnia 或者 VS Code 的 REST Client 插件来测试。
应用配置小技巧
main.ts 里的 app 对象还能做很多事情,这里介绍几个常用的:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用 CORS(前后端分离时必须开)
app.enableCors();
// 设置全局路由前缀
app.setGlobalPrefix('api');
await app.listen(3000);
}
bootstrap();
设了全局前缀后,所有路由都会加上 /api:
/users→/api/users/users/1→/api/users/1
Note
enableCors()在开发时经常用到。前端跑在 5173 端口,后端跑在 3000 端口,不开 CORS 前端请求会被浏览器拦截。
小结
这一章拆解了 NestJS 脚手架项目的四个核心文件:
main.ts:启动入口,创建应用实例并监听端口app.module.ts:根模块,组装整个应用app.controller.ts:控制器,处理 HTTP 请求app.service.ts:服务,封装业务逻辑
然后动手加了一个用户模块,体验了 NestJS 的开发流程。
你可能已经感受到了:NestJS 的代码组织很清晰。控制器管路由,服务管逻辑,模块管组装。各干各的事,互不干扰。