API Smithy a Database Relazionale
Il generatore connection collega un’API Smithy a un progetto Database Relazionale, iniettando un client Prisma nel contesto del servizio in modo che tutte le implementazioni delle operazioni possano accedere al database.
Prerequisiti
Sezione intitolata “Prerequisiti”Prima di utilizzare questo generatore, assicurati di avere:
- Un progetto
ts#smithy-api(backend TypeScript) - Un progetto
ts#rdb
Utilizzo
Sezione intitolata “Utilizzo”Esegui il Generatore
Sezione intitolata “Esegui 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 backend API Smithy 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 modifica tre file esistenti nel tuo backend API Smithy:
Directorypackages/api/src
- context.ts proprietà
dbaggiunta aServiceContext - handler.ts client Prisma creato all’interno di
lambdaHandler, passato aserviceHandler.handle - local-server.ts client Prisma creato all’interno del gestore delle richieste, passato a
serviceHandler.handle
- context.ts proprietà
Inoltre, aggiorna il target serve-local dell’API per avviare automaticamente il database.
Come Funziona
Sezione intitolata “Come Funziona”ServiceContext
Sezione intitolata “ServiceContext”Il generatore aggiunge una proprietà db tipizzata a ServiceContext in context.ts:
import { getPrisma as getMyDb } from ':my-scope/my-db';
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>;}Lambda Handler
Sezione intitolata “Lambda Handler”Il client Prisma viene istanziato all’interno di lambdaHandler e passato attraverso il contesto del servizio:
import { getPrisma as getMyDb } from ':my-scope/my-db';
export const lambdaHandler = async (event: APIGatewayProxyEvent) => { const httpRequest = convertEvent(event); const myDb = await getMyDb(); const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, }); return convertVersion1Response(httpResponse);};Utilizzo del Database nelle Operazioni
Sezione intitolata “Utilizzo del Database nelle Operazioni”Accedi a db dal contesto nelle tue implementazioni delle operazioni:
import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js';
export const listUsers = async ( input: ListUsersOperationInput, ctx: ServiceContext,): Promise<ListUsersOperationOutput> => { const users = await ctx.myDb.user.findMany(); return { users };};Database Multipli
Sezione intitolata “Database Multipli”Eseguendo nuovamente il generatore con una destinazione diversa, il secondo database viene aggiunto accanto al primo. Entrambi i client vengono aggiunti a ServiceContext e istanziati in handler.ts:
export interface ServiceContext { tracer: Tracer; logger: Logger; metrics: Metrics; myDb: Awaited<ReturnType<typeof getMyDb>>; otherDb: Awaited<ReturnType<typeof getOtherDb>>;}const myDb = await getMyDb();const otherDb = await getOtherDb();const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, otherDb,});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.
Requisiti SSL per la Connessione Senza RDS Proxy
Sezione intitolata “Requisiti SSL per la Connessione 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 applica la stessa iniezione del client Prisma all’interno del gestore delle richieste in local-server.ts:
import { getPrisma as getMyDb } from ':my-scope/my-db';
const server = createServer(async function (req, res) { const httpRequest = convertRequest(req); const myDb = await getMyDb(); const httpResponse = await serviceHandler.handle(httpRequest, { tracer, logger, metrics, myDb, }); return writeResponse(httpResponse, res);});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>Questo avvia sia l’API che il database locale. La variabile d’ambiente SERVE_LOCAL=true viene impostata automaticamente, in modo che il client Prisma si connetta al database Docker locale invece di Aurora.