API Smithy para Banco de Dados Relacional
O gerador connection conecta uma API Smithy a um projeto de Banco de Dados Relacional, injetando um cliente Prisma no contexto do serviço para que todas as implementações de operações possam acessar o banco de dados.
Pré-requisitos
Seção intitulada “Pré-requisitos”Antes de usar este gerador, certifique-se de ter:
- Um projeto
ts#smithy-api(backend TypeScript) - Um projeto
ts#rdb
Executar o Gerador
Seção intitulada “Executar o Gerador”- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - connection - Preencha os parâmetros obrigatórios
- Clique em
Generate
pnpm nx g @aws/nx-plugin:connectionyarn nx g @aws/nx-plugin:connectionnpx nx g @aws/nx-plugin:connectionbunx nx g @aws/nx-plugin:connectionVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:connection --dry-runyarn nx g @aws/nx-plugin:connection --dry-runnpx nx g @aws/nx-plugin:connection --dry-runbunx nx g @aws/nx-plugin:connection --dry-runSelecione seu projeto de backend da API Smithy como origem e seu projeto de banco de dados relacional como destino.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| sourceProject Obrigatório | string | - | O projeto de origem |
| targetProject Obrigatório | string | - | O projeto de destino para conectar |
| sourceComponent | string | - | O componente de origem para conectar (nome do componente, caminho relativo à raiz do projeto de origem, ou id do gerador). Use '.' para selecionar explicitamente o projeto como origem. |
| targetComponent | string | - | O componente de destino para conectar (nome do componente, caminho relativo à raiz do projeto de destino, ou id do gerador). Use '.' para selecionar explicitamente o projeto como destino. |
Saída do Gerador
Seção intitulada “Saída do Gerador”O gerador modifica três arquivos existentes no backend da sua API Smithy:
Directorypackages/api/src
- context.ts propriedade
dbadicionada aoServiceContext - handler.ts cliente Prisma criado dentro de
lambdaHandler, passado paraserviceHandler.handle - local-server.ts cliente Prisma criado dentro do manipulador de requisição, passado para
serviceHandler.handle
- context.ts propriedade
Além disso, ele atualiza o target serve-local da API para iniciar o banco de dados automaticamente.
Como Funciona
Seção intitulada “Como Funciona”ServiceContext
Seção intitulada “ServiceContext”O gerador adiciona uma propriedade db tipada ao ServiceContext em 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>>;}Lambda Handler
Seção intitulada “Lambda Handler”O cliente Prisma é instanciado dentro de lambdaHandler e passado através do contexto do serviço:
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);};Usando o Banco de Dados nas Operações
Seção intitulada “Usando o Banco de Dados nas Operações”Acesse db do contexto nas implementações de suas operações:
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 };};Múltiplos Bancos de Dados
Seção intitulada “Múltiplos Bancos de Dados”Executar o gerador novamente com um destino diferente adiciona o segundo banco de dados junto com o primeiro. Ambos os clientes são adicionados ao ServiceContext e instanciados em handler.ts:
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>; otherDb: Awaited<ReturnType<typeof getOtherDb>>;}const myDb = await getMyDb();const otherDb = await getOtherDb();const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, otherDb,});Infraestrutura
Seção intitulada “Infraestrutura”Para permitir que sua API se conecte ao banco de dados em tempo de execução, as funções Lambda da API devem ser implantadas na mesma VPC que o banco de dados e receber acesso de rede e IAM.
No stack da sua aplicação, implante a API na mesma VPC que o banco de dados, depois chame allowDefaultPortFrom e grantConnect para abrir o caminho de rede e conceder permissão IAM rds-db:connect a cada handler Lambda:
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);});Implante as funções Lambda da API em uma sub-rede privada com egress, não em uma sub-rede privada isolada. Em tempo de execução, getPrisma() recupera os detalhes de conexão do banco de dados do AWS AppConfig, que é um endpoint de serviço público da AWS que requer acesso à internet de saída.
Passe as saídas do módulo do banco de dados para o módulo da sua API para que ele possa alcançar o banco de dados e ler sua configuração de tempo de execução:
module "my_database" { source = "../../common/terraform/src/app/dbs/my-database" vpc_id = module.vpc.vpc_id database_subnet_ids = module.vpc.private_isolated_subnet_ids lambda_subnet_ids = module.vpc.private_subnet_ids}
module "api" { source = "..." vpc_id = module.vpc.vpc_id private_subnet_ids = module.vpc.private_subnet_ids
appconfig_application_id = module.my_database.appconfig_application_id database_cluster_resource_id = module.my_database.cluster_resource_id database_runtime_user = module.my_database.database_runtime_user database_security_group_id = module.my_database.security_group_id database_port = module.my_database.cluster_port
environment_variables = { RUNTIME_CONFIG_APP_ID = module.my_database.appconfig_application_id }}Implante as funções Lambda da API em sub-redes privadas com egress, não em sub-redes privadas isoladas. Certifique-se de que a role Lambda da API tenha permissão rds-db:connect e que seu grupo de segurança possa alcançar o grupo de segurança do banco de dados na porta do banco de dados.
Requisitos de SSL ao Conectar Sem RDS Proxy
Seção intitulada “Requisitos de SSL ao Conectar Sem RDS Proxy”Para conexões diretas ao cluster Aurora a partir de runtimes Lambda Node.js 20 ou posterior, carregue o pacote de CA do Amazon RDS definindo NODE_EXTRA_CA_CERTS:
const api = new Api(this, 'Api', { integrations: Api.defaultIntegrations(this) .withDefaultOptions({ environment: { NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem', }, }) .build(),});module "api" { source = "..." ...
environment_variables = { NODE_EXTRA_CA_CERTS = "/var/runtime/ca-cert.pem" }}Para mais detalhes, consulte os requisitos SSL/TLS para conexões Amazon RDS do AWS Lambda e a documentação TLS do Amazon RDS Proxy. Ao usar o RDS Proxy, você não precisa configurar o pacote de CA do RDS em sua função Lambda.
Desenvolvimento Local
Seção intitulada “Desenvolvimento Local”O gerador aplica a mesma injeção de cliente Prisma dentro do manipulador de requisição em 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);});pnpm nx serve-local <api-project-name>yarn nx serve-local <api-project-name>npx nx serve-local <api-project-name>bunx nx serve-local <api-project-name>Isso inicia tanto a API quanto o banco de dados local. A variável de ambiente SERVE_LOCAL=true é definida automaticamente, para que o cliente Prisma se conecte ao banco de dados Docker local em vez do Aurora.