Nest(NestJS)基础复习 - 速通版 03
- 2026-10-14 06:08:50
1.1)安装 Nest CLI
全局安装官方脚手架工具,用于快速生成项目:
npm install -g @nestjs/clinest new my-nest-app过程中会提示选择包管理器(pnpm 或 yarn 等),随后自动完成初始化 初始化完成后进入项目目录: cd my-nest-app
main.ts | |
app.module.ts | |
app.controller.ts | |
app.service.ts | |
test/ |
my-nest-app/├── src/│ ├── app.controller.ts # 控制器,处理 HTTP 请求│ ├── app.controller.spec.ts # 控制器的单元测试│ ├── app.module.ts # 模块,Nest 的核心组成部分│ ├── app.service.ts # 服务,承载业务逻辑│ └── main.ts # 项目入口文件,启动 HTTP 服务器└── test/├── app.e2e-spec.ts # e2e 端到端测试└── jest-e2e.json # e2e 测试的 Jest 配置
controllers(控制器):处理 HTTP 请求,定义路由和请求处理方法。
providers(提供者):提供/注册服务(Service),是真正执行业务逻辑的地方。
imports(导入):导入其他模块,从而可以使用其他模块导出的提供者。这是模块间共享功能的方式。
exports(导出):声明本模块中哪些提供者可以被其他模块使用。没有在 exports 中声明的提供者是模块私有的,即使其他模块导入了本模块也无法使用。这实现了模块的封装性。
⚠️ 常见误区:在模块 A 的 providers 注册了 ServiceX,然后在模块 B 中直接注入 ServiceX —— 这会报错,
因为 ServiceX 没有在模块 A 的 exports 中导出,模块 B 无法访问。
正确做法:在模块 A 的 exports 中加入 ServiceX,然后在模块 B 的 imports 中引入模块 A。
2.2)安装 pg
npm install pg @types/pgimport {Injectable, OnModuleDestroy, OnModuleInit} from '@nestjs/common';import { Pool, QueryResult } from 'pg';@Injectable()export class PostgresService implements OnModuleInit, OnModuleDestroy {private pool: Pool;constructor() {this.pool = new Pool({user: 'your-username',host: 'localhost',database: 'your-database',password: 'your-password',port: 5432,});}async onModuleInit() {await this.pool.connect(); // 在模块初始化时连接到数据库console.log('Connected to the PostgreSQL database');}async onModuleDestroy() {await this.pool.end(); // 在模块销毁时关闭连接池console.log('Database connection closed');}async query(text: string, params?: any[]): Promise<QueryResult> {try {const result = await this.pool.query(text, params);return result;} catch (err) {console.error('Error executing query', err);throw err;}}}
import { Module } from '@nestjs/common';import { PostgresService } from './postgres.service';@Module({providers: [PostgresService],exports: [PostgresService], // 导出服务以便其他模块使用})export class PostgresModule {}
import { Module, Controller, Get } from '@nestjs/common';import { PostgresService } from './postgres/postgres.service';@Controller('users')export class UserController {constructor(privatereadonlypostgresService: PostgresService) {}@Get()async getUsers() {const result = await this.postgresService.query('SELECT * FROM users');return result.rows;}}
@Module({imports: [PostgresModule], // 导入pg模块controllers: [UserController],})export classUserModule{}
import { Module } from '@nestjs/common';import { UserModule } from './user.module'; // 假设 UserModule 在同级目录@Module({imports: [UserModule], // 你的用户模块中已经使用了 PostgresModule})export class AppModule {}
pnpm start # nest start背景:很多跨表操作需要保证共同完成后才算完成,否则所有操作需要全部回滚(保证不同表的数据一致性,避免脏数据) —— 这就是事务机制。
3.1)思路:
在 PostgresService 中定义/封装 transaction 方法,接收一个回调函数作为参数。回调成功则提交事务,发生错误则回滚事务。
import {Injectable, OnModuleDestroy, OnModuleInit} from '@nestjs/common';import { Pool, QueryResult, PoolClient } from 'pg';@Injectable()export class PostgresService implements OnModuleInit, OnModuleDestroy {private pool: Pool;constructor() {this.pool = new Pool({user: 'your-username',host: 'localhost',database: 'your-database',password: 'your-password',port: 5432,});}async onModuleInit() {await this.pool.connect(); // 在模块初始化时连接到数据库console.log('Connected to the PostgreSQL database');}async onModuleDestroy() {await this.pool.end(); // 在模块销毁时关闭连接池console.log('Database connection closed');}async query(text: string, params?: any[]): Promise<QueryResult> {try {const result = await this.pool.query(text, params);return result;} catch (err) {console.error('Error executing query', err);throw err;}}/*** 封装事务处理* @param callback 传入一个包含业务逻辑的回调函数,* pg client 实例通过 回调的入参 传出去外部*/async transaction<T>(callback: (client: PoolClient) => Promise<T>): Promise<T> {const client = await this.pool.connect(); // 获取数据库连接try {await client.query('BEGIN'); // 开始事务const result = await callback(client); // 执行业务逻辑await client.query('COMMIT'); // 提交事务return result;} catch (error) {await client.query('ROLLBACK'); // 回滚事务console.error('Transaction failed, rolling back...', error);throw error;} finally {client.release(); // 释放连接回连接池}}}
3.2) transaction 封装思考
transaction 方法接收一个回调函数,回调函数包含使用事务的数据库操作,回调接收 client 对象,
其 API 与 pool.query 类似,可在其中执行 SQL 查询。
其他模块或服务可通过注入 PostgresService 来使用该 transaction 方法。
3.3)示例:在 Controller 中使用事务
import { Controller, Post, Body } from '@nestjs/common';import { PostgresService } from './postgres/postgres.service';@Controller('orders')export class OrderController {constructor(privatereadonlypostgresService: PostgresService) {}@Post('create')async createOrder(@Body() createOrderDto: any) {// 调用事务return this.postgresService.transaction(async (client) => {// 假设订单包含两个 SQL 操作:创建订单和更新库存const orderQuery = `INSERT INTO orders (user_id, product_id, quantity) VALUES ($1, $2, $3) RETURNING *`;// 创建订单const orderResult = await client.query(orderQuery, [createOrderDto.userId,createOrderDto.productId,createOrderDto.quantity,]);const stockUpdateQuery = `UPDATE products SET stock = stock - $1 WHERE id = $2`;// 更新库存await client.query(stockUpdateQuery, [createOrderDto.quantity, createOrderDto.productId]);return orderResult.rows[0]; // 返回创建的订单数据});}}
该例中 createOrder 通过 transaction 执行多个数据库操作(创建订单 + 更新库存),任意操作失败即自动回滚,避免数据不一致。
3.4)事务处理的优势
自动管理事务:只需专注业务逻辑,事务的开始、提交和回滚均由封装好的方法自动处理。
代码简洁:避免重复的事务控制逻辑,代码更简洁清晰。
错误处理清晰:任何异常都会自动回滚事务,且可以捕获和处理异常。