跳转到内容

Smithy API 到关系数据库

connection 生成器将 Smithy API 连接到 关系数据库 项目,将 Prisma 客户端注入到服务上下文中,以便所有操作实现都可以访问数据库。

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

  1. 一个 Smithy TypeScript API 项目(使用 ts#api--framework=smithy 生成)
  2. 一个 ts#rdb 项目
Terminal window
pnpm nx g @aws/nx-plugin:connection
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run

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

参数类型默认值描述
sourceProject 必需string-源项目
targetProject 必需string-要连接到的目标项目
sourceComponent string-要从其连接的源组件(组件名称、相对于源项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为源。
targetComponent string-要连接到的目标组件(组件名称、相对于目标项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为目标。
preferInstallDependencies booleantrue是否在生成器运行后优先安装依赖项。设置为 false 可在批量运行多个生成器时延迟安装(如果后续生成器需要计算 Nx 项目图,仍会运行安装);在最后统一安装一次。

生成器会修改 Smithy API 后端中的三个现有文件:

  • 文件夹packages/api/src
    • context.ts db 属性已添加到 ServiceContext
    • handler.ts Prisma 客户端在 lambdaHandler 内创建,传递给 serviceHandler.handle
    • local-server.ts Prisma 客户端在请求处理程序内创建,传递给 serviceHandler.handle

此外,它还会更新 API 的 dev 目标以自动启动数据库。

生成器在 context.ts 中向 ServiceContext 添加一个类型化的 db 属性:

packages/api/src/context.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
myDb: Awaited<ReturnType<typeof getMyDb>>;
}

Prisma 客户端在 lambdaHandler 内实例化并通过服务上下文传递:

packages/api/src/handler.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
export const lambdaHandler = async (event: APIGatewayProxyEvent) => {
const httpRequest = convertEvent(event);
const myDb = await getMyDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
});
return convertVersion1Response(httpResponse);
};

在操作实现中从上下文访问 db

packages/api/src/operations/list-users.ts
import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';
import { ServiceContext } from '../context.js';
export const listUsers = async (
input: ListUsersOperationInput,
ctx: ServiceContext,
): Promise<ListUsersOperationOutput> => {
const users = await ctx.myDb.user.findMany();
return { users };
};

使用不同的目标再次运行生成器会将第二个数据库添加到第一个数据库旁边。两个客户端都会添加到 ServiceContext 并在 handler.ts 中实例化:

packages/api/src/context.ts
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
myDb: Awaited<ReturnType<typeof getMyDb>>;
otherDb: Awaited<ReturnType<typeof getOtherDb>>;
}
packages/api/src/handler.ts
const myDb = await getMyDb();
const otherDb = await getOtherDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
otherDb,
});

要允许您的 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 服务端点,需要出站互联网访问。

不使用 RDS Proxy 连接时的 SSL 要求

Section titled “不使用 RDS Proxy 连接时的 SSL 要求”

对于从 Node.js 20 或更高版本的 Lambda 运行时直接连接 Aurora 集群,需要通过设置 NODE_EXTRA_CA_CERTS 来加载 Amazon RDS CA 证书包:

packages/infra/src/stacks/application-stack.ts
const api = new Api(this, 'Api', {
integrations: Api.defaultIntegrations(this)
.withDefaultOptions({
environment: {
NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem',
},
})
.build(),
});

有关更多详细信息,请参阅 AWS Lambda Amazon RDS 连接的 SSL/TLS 要求 和 Amazon RDS Proxy TLS 文档。使用 RDS Proxy 时,您无需在 Lambda 函数中配置 RDS CA 证书包。

生成器在 local-server.ts 的请求处理程序内应用相同的 Prisma 客户端注入:

packages/api/src/local-server.ts
import { getPrisma as getMyDb } from '@my-scope/my-db';
const server = createServer(async function (req, res) {
const httpRequest = convertRequest(req);
const myDb = await getMyDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
});
return writeResponse(httpResponse, res);
});
Terminal window
pnpm nx dev <api-project-name>

这会同时启动 API 和本地数据库。LOCAL_DEV=true 环境变量会自动设置,因此 Prisma 客户端会连接到本地 Docker 数据库而不是 Aurora。