大家好,我是大布布将军。
在上一篇中,我们的 BFF 服务还是一个"中介",它不存储自己的数据。但一个完整的全栈应用,或一个复杂的 BFF,往往需要存储一些自身特有的数据,比如会话信息、BFF 配置、或者一些临时业务数据。这就要求我们掌握数据库知识。
本篇,我们聚焦于最主流的数据库类型:关系型数据库(如 MySQL, PostgreSQL) ,以及如何在 Node.js 中优雅地操作它们。
1. 关系型数据库(SQL)基础:后端思维的基石
关系型数据库的核心是"表"(Table),数据以行和列的方式存储,并通过键(Keys) 建立关联关系(Relationship)。这是后端工程师的"语言"。
| SQL 基础概念 | 对应 Node.js 实践 | 作用 |
|---|---|---|
| CRUD | 增 (POST), 查 (GET), 改 (PUT/PATCH), 删 (DELETE) | 数据库操作的四个基本动作,与 RESTful API 一一对应。 |
| JOIN | 关联查询(如左连接 LEFT JOIN) | 在 Service 层中,通过一次查询获取关联表的数据,避免多次请求。 |
| WHERE | 过滤条件 | 在 Service 层中,根据用户 ID、状态等进行精确查询。 |
【思考转变】 :作为前端,我们习惯的是操作对象(JSON)。作为后端,你必须学会用 SQL 语句思考数据之间的关系。
2. Node.js 操作数据库:告别手动拼接 SQL
在早期的 Node.js 开发中,开发者需要手动拼接 SQL 字符串,比如 'SELECT * FROM users WHERE status = "' + status + '"'。这不仅低效,而且容易导致SQL 注入等严重安全问题。
现代 Node.js 后端开发几乎全部使用 ORM (Object-Relational Mapping) ,即对象关系映射。
- ORM 的作用: 它将数据库中的"表"映射成我们熟悉的 TypeScript/JavaScript 对象(Entity) ,让我们能用面向对象的方式(例如
userRepository.find({ where: { status: 'active' } }))来操作数据库,而不需要写一行 SQL。 - 主流选择: 推荐 TypeORM (功能强大,与 NestJS 结合紧密)或 Prisma(近年新兴的 ORM,体验现代化)。
3. 实践项目:基于 TypeORM 的 CRUD 模块
我们以 TypeORM 为例,来实现对 Order 数据的基本 CRUD 操作。
步骤一:定义实体 (Entity)
在 TypeORM 中,我们使用 TypeScript 类来定义数据库中的表结构:
TypeScript
// 📁 src/order/order.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
@Entity('orders') // 对应数据库中的 orders 表
export class Order {
@PrimaryGeneratedColumn()
id: number;
@Column()
userId: number; // 哪个用户下的订单
@Column({ type: 'decimal', precision: 10, scale: 2 })
totalAmount: number;
@Column({ default: 'pending' })
status: 'pending' | 'paid' | 'shipped';
@CreateDateColumn()
createdAt: Date; // 自动创建时间
}
步骤二:在 Service 层执行 CRUD
在 Service 层中,我们注入 Repository 来进行操作:
TypeScript
// 📁 src/order/order.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from './order.entity';
@Injectable()
export class OrderService {
// 依赖注入:获取 Order 表的操作句柄 (Repository)
constructor(
@InjectRepository(Order)
private orderRepository: Repository<Order>,
) {}
// 1. C - Create (创建订单)
async createOrder(data: Partial<Order>): Promise<Order> {
const newOrder = this.orderRepository.create(data); // 实例化实体
return this.orderRepository.save(newOrder); // 保存到数据库
}
// 2. R - Read (读取订单详情)
async findOrderById(id: number): Promise<Order | null> {
// 使用 findOne 方法,以 WHERE 条件查询
return this.orderRepository.findOne({ where: { id: id, status: 'paid' } });
}
// 3. U - Update (更新订单状态)
async updateOrderStatus(id: number, status: 'shipped'): Promise<void> {
await this.orderRepository.update(id, { status: status });
}
}
4. 高级应用:JOIN 查询与数据关联
在实际业务中,我们经常需要同时获取订单和其对应的用户信息。
在 ORM 中,我们通常通过在实体上定义关系(例如 @OneToOne, @OneToMany)来实现 JOIN 查询,从而仅用一次数据库操作 就取回所有关联数据。这比在 Service 层写两个 findOne 再手动合并数据高效得多。
核心要点: 当你需要的数据分散在两个或更多表时,优先考虑使用 ORM 的关联查询(本质是 SQL JOIN)。
总结
掌握 SQL 基础和 ORM 是从 BFF 迈向全栈的决定性一步。它让你从一个 API 编排者,升级为一个数据所有者和管理者 。

通过 ORM,我们安全且优雅地完成了数据的持久化操作。下一篇,我们将探讨如何利用 NoSQL 数据库(Redis) ,将数据存储的效率进一步提升,实现 BFF 接口的高性能缓存。