Pular para o conteúdo

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.

Antes de usar este gerador, certifique-se de ter:

  1. Um projeto ts#trpc-api
  2. Um projeto ts#rdb
  1. Instale o Nx Console VSCode Plugin se ainda não o fez
  2. Abra o console Nx no VSCode
  3. Clique em Generate (UI) na seção "Common Nx Commands"
  4. Procure por @aws/nx-plugin - connection
  5. Preencha os parâmetros obrigatórios
    • Clique em Generate

    Selecione 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.

    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.

    Adicione o plugin gerado ao seu roteador tRPC para que todos os procedimentos que o utilizam ganhem acesso ao banco de dados:

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

    O plugin mescla IMyDbContext no contexto do seu procedimento, disponibilizando myDb como uma propriedade opcional:

    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 é o cliente Prisma — tipado como Awaited<ReturnType<typeof getPrisma>>
    return await ctx.myDb!.user.findMany();
    });

    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():

    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();
    }
    });

    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.

    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:

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

    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:

    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);
    });

    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.

    O gerador configura o target serve-local da sua API tRPC para depender do target serve-local do banco de dados, então executar:

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

    iniciará automaticamente o banco de dados local junto com sua API.