API tRPC para Banco de Dados Relacional
O gerador connection conecta uma API tRPC a um projeto de Banco de Dados Relacional, gerando um plugin de middleware tRPC com segurança de tipos que disponibiliza um cliente Prisma no contexto do seu procedimento.
Pré-requisitos
Seção intitulada “Pré-requisitos”Antes de usar este gerador, certifique-se de ter:
- Um projeto
ts#trpc-api - 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 API tRPC 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 cria um arquivo de middleware no seu projeto de API tRPC:
Directorypackages/api/src
Directorymiddleware
- <db-name>.ts plugin tRPC expondo o cliente Prisma no contexto do procedimento
Além disso, ele atualiza o target serve-local da sua API tRPC para iniciar o banco de dados automaticamente ao executar localmente.
Usando o Middleware
Seção intitulada “Usando o Middleware”Registrar o Plugin
Seção intitulada “Registrar o Plugin”Adicione o plugin gerado ao seu roteador tRPC para que todos os procedimentos que o utilizam ganhem acesso ao banco de dados:
import { t } from './init.js';import { createMyDbPlugin } from './middleware/my-db.js';
export const authenticatedProcedure = t.procedure .concat(createMyDbPlugin());Acessar o Banco de Dados nos Procedimentos
Seção intitulada “Acessar o Banco de Dados nos Procedimentos”O plugin mescla IMyDbContext no contexto do seu procedimento, disponibilizando myDb como uma propriedade opcional:
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 é o cliente Prisma — tipado como Awaited<ReturnType<typeof getPrisma>> return await ctx.myDb!.user.findMany(); });MySQL: Desconectar Após Cada Requisição
Seção intitulada “MySQL: Desconectar Após Cada Requisição”Quando o banco de dados de destino usa o motor MySQL, o middleware gerado envolve opts.next() em um bloco try/finally que chama $disconnect():
return t.procedure.use(async (opts) => { const myDb = await getPrisma(); try { return await opts.next({ ctx: { ...opts.ctx, myDb } }); } finally { await myDb.$disconnect(); }});Isso resolve o problema do adaptador MySQL manter o event loop do Node.js aberto após uma consulta, o que de outra forma impediria o Lambda de liberar respostas em streaming. Desconectar no finally libera o event loop para que a resposta possa ser concluída. Consulte MySQL: Modo de Streaming do API Gateway para detalhes.
O PostgreSQL não requer isso — seu adaptador usa um pool de conexões configurado com allowExitOnIdle: true.
Múltiplos Bancos de Dados
Seção intitulada “Múltiplos Bancos de Dados”Você pode conectar bancos de dados adicionais executando o gerador novamente com um destino diferente. Cada banco de dados obtém seu próprio plugin e interface de contexto:
export const dbProcedure = t.procedure .concat(createPostgresDbPlugin()) .concat(createMySqlDbPlugin());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.
Desenvolvimento Local
Seção intitulada “Desenvolvimento Local”O gerador configura o target serve-local da sua API tRPC para depender do target serve-local do banco de dados, então executar:
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>iniciará automaticamente o banco de dados local junto com sua API.