Ir al contenido

API tRPC a Base de Datos Relacional

El generador connection conecta una API tRPC a un proyecto de Base de Datos Relacional, generando un plugin de middleware tRPC con tipos seguros que hace que un cliente Prisma esté disponible en el contexto de tu procedimiento.

Antes de usar este generador, asegúrate de tener:

  1. Un proyecto ts#trpc-api
  2. Un proyecto ts#rdb
  1. Instale el Nx Console VSCode Plugin si aún no lo ha hecho
  2. Abra la consola Nx en VSCode
  3. Haga clic en Generate (UI) en la sección "Common Nx Commands"
  4. Busque @aws/nx-plugin - connection
  5. Complete los parámetros requeridos
    • Haga clic en Generate

    Selecciona tu proyecto de API tRPC como origen y tu proyecto de base de datos relacional como destino.

    Parámetro Tipo Predeterminado Descripción
    sourceProject Requerido string - El proyecto de origen
    targetProject Requerido string - El proyecto de destino al que conectar
    sourceComponent string - El componente de origen desde el que conectar (nombre del componente, ruta relativa a la raíz del proyecto de origen, o id del generador). Use '.' para seleccionar explícitamente el proyecto como origen.
    targetComponent string - El componente de destino al que conectar (nombre del componente, ruta relativa a la raíz del proyecto de destino, o id del generador). Use '.' para seleccionar explícitamente el proyecto como destino.

    El generador crea un archivo de middleware en tu proyecto de API tRPC:

    • Directoriopackages/api/src
      • Directoriomiddleware
        • <db-name>.ts plugin tRPC que expone el cliente Prisma en el contexto del procedimiento

    Además, actualiza el target serve-local de tu API tRPC para iniciar la base de datos automáticamente al ejecutarse localmente.

    Agrega el plugin generado a tu router tRPC para que todos los procedimientos que lo usen obtengan acceso a la base de datos:

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

    Acceder a la Base de Datos en los Procedimientos

    Sección titulada «Acceder a la Base de Datos en los Procedimientos»

    El plugin fusiona IMyDbContext en el contexto de tu procedimiento, haciendo que myDb esté disponible como una propiedad 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 es el cliente Prisma — tipado como Awaited<ReturnType<typeof getPrisma>>
    return await ctx.myDb!.user.findMany();
    });

    MySQL: Desconectar Después de Cada Solicitud

    Sección titulada «MySQL: Desconectar Después de Cada Solicitud»

    Cuando la base de datos de destino usa el motor MySQL, el middleware generado envuelve opts.next() en un bloque try/finally que llama a $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();
    }
    });

    Esto soluciona el problema del adaptador MySQL que mantiene abierto el bucle de eventos de Node.js después de una consulta, lo que de otro modo evitaría que Lambda vacíe las respuestas de streaming. Desconectar en finally libera el bucle de eventos para que la respuesta pueda completarse. Consulta MySQL: Modo de Streaming de API Gateway para más detalles.

    PostgreSQL no requiere esto — su adaptador usa un pool de conexiones configurado con allowExitOnIdle: true.

    Puedes conectar bases de datos adicionales ejecutando el generador nuevamente con un destino diferente. Cada base de datos obtiene su propio plugin e interfaz de contexto:

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

    Para permitir que tu API se conecte a la base de datos en tiempo de ejecución, las funciones Lambda de la API deben desplegarse en la misma VPC que la base de datos y se les debe otorgar acceso de red e IAM.

    En tu stack de aplicación, despliega la API en la misma VPC que la base de datos, luego llama a allowDefaultPortFrom y grantConnect para abrir la ruta de red y otorgar el permiso IAM rds-db:connect a cada manejador 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);
    });

    Despliega las funciones Lambda de la API en una subred privada con salida, no en una subred privada aislada. En tiempo de ejecución, getPrisma() recupera los detalles de conexión de la base de datos desde AWS AppConfig, que es un endpoint de servicio público de AWS que requiere acceso a internet saliente.

    El generador configura el target serve-local de tu API tRPC para que dependa del target serve-local de la base de datos, por lo que ejecutar:

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

    iniciará automáticamente la base de datos local junto con tu API.