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:
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. |
| preferInstallDependencies | boolean | true | Se preferire l'installazione delle dipendenze dopo l'esecuzione del generatore. Impostare su false per rimandare l'installazione quando si eseguono più generatori in batch (l'installazione viene comunque eseguita se necessaria affinché i generatori successivi possano calcolare il grafo dei progetti Nx); installare una volta alla fine. |
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 dev 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(createMyDbPlugin()) .concat(createOtherDbPlugin());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.
Distribuisci l’API nello stesso VPC del database, concedigli rds-db:connect tramite additional_iam_policy_statements, e apri il percorso di rete con una coppia di regole di security group. Le risorse aws_vpc.main e aws_subnet sono definite nella guida di distribuzione del database:
module "my_database" { source = "../../common/terraform/src/app/dbs/my-database" vpc_id = aws_vpc.main.id database_subnet_ids = aws_subnet.database[*].id lambda_subnet_ids = aws_subnet.private[*].id}
module "api" { source = "../../common/terraform/src/app/apis/my-api" enable_vpc = true vpc_id = aws_vpc.main.id subnet_ids = aws_subnet.private[*].id
additional_iam_policy_statements = [ { Effect = "Allow" Action = ["rds-db:connect"] Resource = [ "arn:aws:rds-db:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:dbuser:${module.my_database.connect_resource_id}/${module.my_database.database_runtime_user}" ] }, { Effect = "Allow" Action = [ "appconfig:StartConfigurationSession", "appconfig:GetLatestConfiguration" ] Resource = [ "${module.runtime_config_appconfig.application_arn}/*" ] } ]
env = { RUNTIME_CONFIG_APP_ID = module.runtime_config_appconfig.application_id }}
resource "aws_vpc_security_group_ingress_rule" "api_to_database" { description = "Allow the API Lambda functions to connect to the database" security_group_id = module.my_database.security_group_id referenced_security_group_id = module.api.security_group_id from_port = module.my_database.cluster_port to_port = module.my_database.cluster_port ip_protocol = "tcp"}
resource "aws_vpc_security_group_egress_rule" "api_to_database" { description = "Allow outbound traffic from the API Lambda functions to the database" security_group_id = module.api.security_group_id referenced_security_group_id = module.my_database.security_group_id from_port = module.my_database.cluster_port to_port = module.my_database.cluster_port ip_protocol = "tcp"}Distribuisci le funzioni Lambda dell’API in subnet private con egress, non in subnet private isolate. appconfig_application_id/appconfig_application_arn provengono dall’applicazione AppConfig di configurazione runtime condivisa dichiarata una volta nel tuo modulo root, non dal modulo database — passarli imposta RUNTIME_CONFIG_APP_ID sulle funzioni Lambda e concede loro accesso in lettura all’applicazione. Includi il namespace database quando lo istanzi in modo che venga distribuita la voce di configurazione runtime del modulo database:
module "runtime_config_appconfig" { source = "../../common/terraform/src/core/runtime-config/appconfig"
application_name = "my-app-runtime-config" namespaces = ["connection", "agentcore", "database"]}Requisiti SSL Quando ci si Connette Senza RDS Proxy
Sezione intitolata “Requisiti SSL Quando ci si Connette Senza RDS Proxy”Per le connessioni dirette al cluster Aurora dai runtime Lambda Node.js 20 o successivi, carica il bundle CA di Amazon RDS impostando NODE_EXTRA_CA_CERTS:
const api = new Api(this, 'Api', { integrations: Api.defaultIntegrations(this) .withDefaultOptions({ environment: { NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem', }, }) .build(),});module "api" { source = "..." ...
environment_variables = { NODE_EXTRA_CA_CERTS = "/var/runtime/ca-cert.pem" }}Per maggiori dettagli, consulta i requisiti SSL/TLS per le connessioni Amazon RDS di AWS Lambda e la documentazione TLS di Amazon RDS Proxy. Quando si utilizza RDS Proxy, non è necessario configurare il bundle CA di RDS nella funzione Lambda.
Sviluppo Locale
Sezione intitolata “Sviluppo Locale”Il generatore configura il target dev della tua API tRPC per dipendere dal target dev del database, quindi eseguendo:
pnpm nx dev <api-project-name>yarn nx dev <api-project-name>npx nx dev <api-project-name>bunx nx dev <api-project-name>avvierà automaticamente il database locale insieme alla tua API.