跳转到内容

tRPC API 连接到关系数据库

connection 生成器将 tRPC API 连接到 关系数据库 项目,生成一个类型安全的 tRPC 中间件插件,使 Prisma 客户端在您的过程上下文中可用。

在使用此生成器之前,请确保您拥有:

  1. 一个 ts#trpc-api 项目
  2. 一个 ts#rdb 项目
  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - connection
  5. 填写必需参数
    • 点击 Generate

    选择您的 tRPC API 项目作为源,选择您的关系数据库项目作为目标。

    参数 类型 默认值 描述
    sourceProject 必需 string - 源项目
    targetProject 必需 string - 要连接到的目标项目
    sourceComponent string - 要从其连接的源组件(组件名称、相对于源项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为源。
    targetComponent string - 要连接到的目标组件(组件名称、相对于目标项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为目标。

    生成器在您的 tRPC API 项目中创建一个中间件文件:

    • 文件夹packages/api/src
      • 文件夹middleware
        • <db-name>.ts 在过程上下文中公开 Prisma 客户端的 tRPC 插件

    此外,它还会更新您的 tRPC API 的 serve-local 目标,以便在本地运行时自动启动数据库。

    将生成的插件添加到您的 tRPC 路由器,以便所有使用它的过程都能访问数据库:

    packages/api/src/router.ts
    import { t } from './init.js';
    import { createMyDbPlugin } from './middleware/my-db.js';
    export const authenticatedProcedure = t.procedure
    .concat(createMyDbPlugin());

    该插件将 IMyDbContext 合并到您的过程上下文中,使 myDb 作为可选属性可用:

    packages/api/src/procedures/users.ts
    import { z } from 'zod';
    import { authenticatedProcedure } from '../router.js';
    export const listUsers = authenticatedProcedure
    .output(z.array(z.object({ id: z.string(), name: z.string() })))
    .query(async ({ ctx }) => {
    // ctx.myDb 是 Prisma 客户端 — 类型为 Awaited<ReturnType<typeof getPrisma>>
    return await ctx.myDb!.user.findMany();
    });

    当目标数据库使用 MySQL 引擎时,生成的中间件会将 opts.next() 包装在调用 $disconnect()try/finally 块中:

    packages/api/src/middleware/my-db.ts
    return t.procedure.use(async (opts) => {
    const myDb = await getPrisma();
    try {
    return await opts.next({ ctx: { ...opts.ctx, myDb } });
    } finally {
    await myDb.$disconnect();
    }
    });

    这解决了 MySQL 适配器在查询后保持 Node.js 事件循环打开的问题,否则会阻止 Lambda 刷新流式响应。在 finally 中断开连接会释放事件循环,以便响应可以完成。详情请参阅 MySQL: API Gateway 流式模式

    PostgreSQL 不需要这样做 — 其适配器使用配置了 allowExitOnIdle: true 的连接池。

    您可以通过使用不同的目标再次运行生成器来连接其他数据库。每个数据库都有自己的插件和上下文接口:

    packages/api/src/router.ts
    export const dbProcedure = t.procedure
    .concat(createPostgresDbPlugin())
    .concat(createMySqlDbPlugin());

    为了让您的 API 在运行时能够连接到数据库,API Lambda 函数必须部署到与数据库相同的 VPC 中,并被授予网络和 IAM 访问权限。

    在您的应用程序堆栈中,将 API 部署到与数据库相同的 VPC 中,然后调用 allowDefaultPortFromgrantConnect 来打开网络路径并为每个 Lambda 处理程序授予 IAM rds-db:connect 权限:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', { vpc, ... });
    const api = new MyApi(this, 'Api', {
    integrations: MyApi.defaultIntegrations(this)
    .withDefaultOptions({
    vpc,
    vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
    })
    .build(),
    });
    Object.entries(api.integrations).forEach(([operation, integration]) => {
    db.allowDefaultPortFrom(integration.handler, `Allow ${operation} to connect to the database`);
    db.grantConnect(integration.handler);
    });

    将 API Lambda 函数部署到具有出口的私有子网中,而不是私有隔离子网。在运行时,getPrisma() 从 AWS AppConfig 检索数据库连接详细信息,这是一个需要出站互联网访问的公共 AWS 服务端点。

    生成器将您的 tRPC API 的 serve-local 目标配置为依赖于数据库的 serve-local 目标,因此运行:

    Terminal window
    pnpm nx serve-local <api-project-name>

    将自动在您的 API 旁边启动本地数据库。