API tRPC a Database Relazionale
Il generatore connection collega un’API tRPC a un progetto Database Relazionale, generando un plugin middleware tRPC type-safe che rende disponibile un client Prisma nel contesto delle tue procedure.
Prerequisiti
Sezione intitolata “Prerequisiti”Prima di utilizzare questo generatore, assicurati di avere:
- Un progetto
ts#trpc-api - Un progetto
ts#rdb
Utilizzo
Sezione intitolata “Utilizzo”Eseguire il Generatore
Sezione intitolata “Eseguire il Generatore”- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - connection - Compila i parametri richiesti
- Clicca su
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:connectionPuoi anche eseguire una prova per vedere quali file verrebbero modificati
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-runSeleziona il tuo progetto API tRPC come sorgente e il tuo progetto database relazionale come destinazione.
Opzioni
Sezione intitolata “Opzioni”| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
| sourceProject Obbligatorio | string | - | Il progetto sorgente |
| targetProject Obbligatorio | string | - | Il progetto di destinazione a cui connettersi |
| sourceComponent | string | - | Il componente sorgente da cui connettersi (nome del componente, percorso relativo alla radice del progetto sorgente, o id del generatore). Usare '.' per selezionare esplicitamente il progetto come sorgente. |
| targetComponent | string | - | Il componente di destinazione a cui connettersi (nome del componente, percorso relativo alla radice del progetto di destinazione, o id del generatore). Usare '.' per selezionare esplicitamente il progetto come destinazione. |
Output del Generatore
Sezione intitolata “Output del Generatore”Il generatore crea un file middleware nel tuo progetto API tRPC:
Directorypackages/api/src
Directorymiddleware
- <db-name>.ts plugin tRPC che espone il client Prisma nel contesto delle procedure
Inoltre, aggiorna il target serve-local della tua API tRPC per avviare automaticamente il database quando viene eseguito localmente.
Utilizzo del Middleware
Sezione intitolata “Utilizzo del Middleware”Registrare il Plugin
Sezione intitolata “Registrare il Plugin”Aggiungi il plugin generato al tuo router tRPC in modo che tutte le procedure che lo utilizzano ottengano accesso al database:
import { t } from './init.js';import { createMyDbPlugin } from './middleware/my-db.js';
export const authenticatedProcedure = t.procedure .concat(createMyDbPlugin());Accedere al Database nelle Procedure
Sezione intitolata “Accedere al Database nelle Procedure”Il plugin unisce IMyDbContext nel contesto delle tue procedure, rendendo myDb disponibile come proprietà opzionale:
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 è il client Prisma — tipizzato come Awaited<ReturnType<typeof getPrisma>> return await ctx.myDb!.user.findMany(); });MySQL: Disconnessione Dopo Ogni Richiesta
Sezione intitolata “MySQL: Disconnessione Dopo Ogni Richiesta”Quando il database di destinazione utilizza il motore MySQL, il middleware generato avvolge opts.next() in un blocco try/finally che chiama $disconnect():
return t.procedure.use(async (opts) => { const myDb = await getPrisma(); try { return await opts.next({ ctx: { ...opts.ctx, myDb } }); } finally { await myDb.$disconnect(); }});Questo risolve il problema dell’adapter MySQL che mantiene aperto l’event loop di Node.js dopo una query, il che altrimenti impedirebbe a Lambda di inviare le risposte in streaming. Disconnettersi nel blocco finally rilascia l’event loop in modo che la risposta possa completarsi. Vedi MySQL: Modalità Streaming API Gateway per i dettagli.
PostgreSQL non richiede questo — il suo adapter utilizza un connection pool configurato con allowExitOnIdle: true.
Database Multipli
Sezione intitolata “Database Multipli”Puoi connettere database aggiuntivi eseguendo nuovamente il generatore con una destinazione diversa. Ogni database ottiene il proprio plugin e interfaccia di contesto:
export const dbProcedure = t.procedure .concat(createPostgresDbPlugin()) .concat(createMySqlDbPlugin());Infrastruttura
Sezione intitolata “Infrastruttura”Per consentire alla tua API di connettersi al database a runtime, le funzioni Lambda dell’API devono essere distribuite nello stesso VPC del database e devono avere accesso di rete e IAM.
Nel tuo stack applicativo, distribuisci l’API nello stesso VPC del database, quindi chiama allowDefaultPortFrom e grantConnect per aprire il percorso di rete e concedere il permesso IAM rds-db:connect a ciascun 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);});Distribuisci le funzioni Lambda dell’API in una subnet privata con egress, non in una subnet privata isolata. A runtime, getPrisma() recupera i dettagli di connessione al database da AWS AppConfig, che è un endpoint pubblico del servizio AWS che richiede accesso internet in uscita.
Passa gli output del modulo database nel tuo modulo API in modo che possa raggiungere il database e leggere la sua configurazione runtime:
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 }}Distribuisci le funzioni Lambda dell’API in subnet private con egress, non in subnet private isolate. Assicurati che il ruolo Lambda dell’API abbia il permesso rds-db:connect e che il suo security group possa raggiungere il security group del database sulla porta del database.
Sviluppo Locale
Sezione intitolata “Sviluppo Locale”Il generatore configura il target serve-local della tua API tRPC per dipendere dal target serve-local del database, quindi eseguendo:
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>avvierà automaticamente il database locale insieme alla tua API.