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.
Requisitos Previos
Sección titulada «Requisitos Previos»Antes de usar este generador, asegúrate de tener:
- Un proyecto
ts#trpc-api - Un proyecto
ts#rdb
Ejecutar el Generador
Sección titulada «Ejecutar el Generador»- Instale el Nx Console VSCode Plugin si aún no lo ha hecho
- Abra la consola Nx en VSCode
- Haga clic en
Generate (UI)en la sección "Common Nx Commands" - Busque
@aws/nx-plugin - connection - Complete los parámetros requeridos
- Haga clic en
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:connectionTambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
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-runSelecciona tu proyecto de API tRPC como origen y tu proyecto de base de datos relacional como destino.
Opciones
Sección titulada «Opciones»| 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. |
Salida del Generador
Sección titulada «Salida del Generador»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.
Usar el Middleware
Sección titulada «Usar el Middleware»Registrar el Plugin
Sección titulada «Registrar el Plugin»Agrega el plugin generado a tu router tRPC para que todos los procedimientos que lo usen obtengan acceso a la base de datos:
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:
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():
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.
Múltiples Bases de Datos
Sección titulada «Múltiples Bases de Datos»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:
export const dbProcedure = t.procedure .concat(createPostgresDbPlugin()) .concat(createMySqlDbPlugin());Infraestructura
Sección titulada «Infraestructura»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:
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.
Pasa las salidas del módulo de base de datos a tu módulo de API para que pueda alcanzar la base de datos y leer su configuración en tiempo de ejecución:
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 }}Despliega las funciones Lambda de la API en subredes privadas con salida, no en subredes privadas aisladas. Asegúrate de que el rol Lambda de la API tenga el permiso rds-db:connect y que su grupo de seguridad pueda alcanzar el grupo de seguridad de la base de datos en el puerto de la base de datos.
Desarrollo Local
Sección titulada «Desarrollo Local»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:
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á automáticamente la base de datos local junto con tu API.