从“Hello World”到企业级后端:用 NestJS 打造你的第一个 RESTful API(附 PostgreSQL 实战)

6 阅读4分钟

“写代码就像谈恋爱——一开始都是 Hello,最后都得连数据库。”
—— 某不愿透露姓名的全栈工程师


🌟 开篇:为什么是 NestJS?

你是不是也经历过这样的场景:

  • 用 Express 写了个小项目,结果业务一复杂,代码像意大利面条一样缠在一起?
  • 想重构?不敢动!因为不知道改一行会不会崩掉整个服务。
  • 团队协作时,有人用 snake_case,有人用 camelCase,还有人直接拼音命名……

这时候,你就需要一个有纪律、有结构、有 TypeScript 加持的企业级框架——NestJS

它不像 Express 那样“自由奔放”,而是像一位穿着西装、打着领带的架构师,温柔又坚定地告诉你:“孩子,代码要这样写。


🧱 什么是 NestJS?一句话说清楚

NestJS = Angular 的架构思想 + Express/Fastify 的底层 + TypeScript 的类型安全 + 依赖注入 + 模块化设计

它不是又一个轮子,而是一套工程化思维的落地实践。尤其适合中大型项目、多人协作、长期维护。


🛠️ 极简上手:5 分钟跑起你的第一个 Nest 项目

# 全局安装 CLI(一次就行)
npm i -g @nestjs/cli

# 创建新项目(比如叫 nest-test-demo)
nest new nest-test-demo

# 进入目录 & 启动
cd nest-test-demo
npm run start:dev

访问 http://localhost:3000,看到 "Hello World!"?恭喜你,已经站在了企业级开发的门口!

💡 小贴士:建议把端口改成 .env 文件配置,更优雅:

PORT=1234

🧩 核心概念三件套:Module / Controller / Service

NestJS 的灵魂,就是这三个角色:

角色职责类比
Module组织功能单元,声明谁和谁能一起玩包工头:负责协调资源
Controller处理 HTTP 请求,路由入口前台接待:接收客户请求
Service实现业务逻辑,操作数据后厨大厨:真正干活的人

✅ 举个栗子:Todo 列表 API

我们来实现一个简单的 Todo 系统:

  • GET /todos → 获取所有任务
  • POST /todos → 新增任务
  • DELETE /todos/:id → 删除任务

1. Service:业务逻辑集中地

// todos.service.ts
@Injectable()
export class TodosService {
  private todos: Todo[] = [
    { id: 1, title: "周五狂欢", completed: false },
    { id: 2, title: "三角洲首胜", completed: false }
  ];

  findAll() { return this.todos; }

  addTodo(title: string) {
    const todo = { id: Date.now(), title, completed: false };
    this.todos.push(todo);
    return todo;
  }

  deleteTodo(id: number) {
    this.todos = this.todos.filter(t => t.id !== id);
    return { message: 'Todo deleted', code: 200 };
  }
}

🎯 注意:这里用了 @Injectable(),表示这个类可以被“注入”到其他地方使用。

2. Controller:HTTP 接口定义

// todos.controller.ts
@Controller('todos')
export class TodosController {
  constructor(private readonly todosService: TodosService) {}

  @Get()
  getTodos() {
    return this.todosService.findAll();
  }

  @Post()
  addTodo(@Body('title') title: string) {
    return this.todosService.addTodo(title);
  }

  @Delete(':id')
  deleteTodo(@Param('id', ParseIntPipe) id: number) {
    return this.todosService.deleteTodo(id);
  }
}

🔍 亮点:

  • @Body('title'):只取 title 字段,避免多余参数污染
  • ParseIntPipe:自动把字符串 '123' 转成数字 123,再也不怕类型错误!

3. Module:把它们组装起来

// todos.module.ts
@Module({
  controllers: [TodosController],
  providers: [TodosService]
})
export class TodosModule {}

然后在 AppModule 中导入:

@Module({
  imports: [TodosModule],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule {}

搞定!现在你的 API 已经支持完整的 CRUD 了。


🗄️ 连接真实数据库:PostgreSQL 实战

内存里的 Todo 刷新就没了?那不行!我们得连上真正的数据库。

第一步:创建全局 DatabaseModule

// database/database.module.ts
import { Module, Global } from '@nestjs/common';
import { Pool } from 'pg';
import * as dotenv from 'dotenv';
dotenv.config();

@Global() // 全局可用,不用到处 import
@Module({
  providers: [
    {
      provide: 'PG_CONNECTION',
      useValue: new Pool({
        user: process.env.DB_USER,
        host: process.env.DB_HOST,
        database: process.env.DB_NAME,
        password: process.env.DB_PASSWORD,
        port: parseInt(process.env.DB_PORT || '5432', 10),
      })
    }
  ],
  exports: ['PG_CONNECTION']
})
export class DatabaseModule {}

💡 为什么用 provide: 'PG_CONNECTION'
这是一种自定义 Provider的方式,通过字符串 Token 注入,简单直接(适合小型项目)。进阶可用 Class 或工厂模式。

第二步:在 Controller 中使用数据库

// app.controller.ts
@Controller()
export class AppController {
  constructor(
    @Inject('PG_CONNECTION') private readonly db: any,
    private readonly appService: AppService
  ) {}

  @Get('db-test')
  async testConnection() {
    try {
      const res = await this.db.query('SELECT * FROM users');
      return { status: '连接成功', data: res.rows };
    } catch (error) {
      return { status: '连接失败', error: error.message };
    }
  }
}

记得在 .env 里配好数据库信息:

DB_USER=postgres
DB_HOST=localhost
DB_NAME=xuebi
DB_PASSWORD=
DB_PORT=5432

⚠️ 安全提醒:生产环境绝不能把密码写在代码里!要用密钥管理或环境变量注入。


🧪 RESTful 设计哲学:语义化你的 API

NestJS 鼓励你写出符合 REST 规范的接口:

方法用途示例
GET获取资源/users
POST创建资源/users
PUT完整更新资源/users/1
PATCH部分更新资源/users/1(只改 nickname)
DELETE删除资源/users/1

🤔 为什么要有 PUT 和 PATCH?
想象你要改微信昵称:

  • PUT:你得把头像、性别、地区全传一遍(哪怕没变)
  • PATCH:只传 { nickname: "码农老张" } 就行!
    PATCH 更省流量,更人性化!

🎭 幽默总结:NestJS 的“职场人设”

  • Express:自由职业者,穿拖鞋写代码,效率高但容易翻车。
  • NestJS:大厂 P7 架构师,西装革履,文档齐全,代码可测可维护。
  • :刚入职的新人,老板说“用 Nest 写个后台”,你连夜啃完这篇教程,第二天自信提交 PR 👏

🚀 结语:下一步学什么?

这篇文章带你从零搭建了一个带数据库的 NestJS 项目。但 Nest 的世界远不止于此:

  • ✅ 使用 TypeORM / Prisma 替代原生 SQL
  • ✅ 添加 DTO + ValidationPipe 做参数校验
  • ✅ 集成 JWT 鉴权
  • ✅ 编写 单元测试 / E2E 测试
  • ✅ 部署到 Docker + Nginx

记住:框架只是工具,真正的高手,是在约束中创造自由。