Pular para o conteúdo

API Smithy para Banco de Dados Relacional

O gerador connection conecta uma API Smithy a um projeto de Banco de Dados Relacional, injetando um cliente Prisma no contexto do serviço para que todas as implementações de operação possam acessar o banco de dados.

Antes de usar este gerador, certifique-se de ter:

  1. Um projeto de API TypeScript Smithy (gerado com ts#api usando --framework=smithy)
  2. Um projeto ts#rdb
Terminal window
pnpm nx g @aws/nx-plugin:connection
Você também pode realizar uma execução simulada para ver quais arquivos seriam alterados
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run

Selecione seu projeto backend de API Smithy como origem e seu projeto de banco de dados relacional como destino.

ParâmetroTipoPadrãoDescrição
sourceProject Obrigatóriostring-O projeto de origem
targetProject Obrigatóriostring-O projeto de destino para conectar
sourceComponent string-O componente de origem para conectar (nome do componente, caminho relativo à raiz do projeto de origem, ou id do gerador). Use '.' para selecionar explicitamente o projeto como origem.
targetComponent string-O componente de destino para conectar (nome do componente, caminho relativo à raiz do projeto de destino, ou id do gerador). Use '.' para selecionar explicitamente o projeto como destino.
preferInstallDependencies booleantrueSe deve preferir instalar dependências após a execução do gerador. Defina como false para adiar a instalação ao executar múltiplos geradores em lote (uma instalação ainda é executada se necessário para que os geradores subsequentes possam calcular o grafo de projetos Nx); instale uma vez no final.

O gerador modifica três arquivos existentes no seu backend de API Smithy:

  • Directorypackages/api/src
    • context.ts propriedade db adicionada ao ServiceContext
    • handler.ts cliente Prisma criado dentro de lambdaHandler, passado para serviceHandler.handle
    • local-server.ts cliente Prisma criado dentro do manipulador de requisição, passado para serviceHandler.handle

Além disso, ele atualiza o target dev da API para iniciar o banco de dados automaticamente.

O gerador adiciona uma propriedade db tipada ao ServiceContext em context.ts:

packages/api/src/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>>;
}

O cliente Prisma é instanciado dentro de lambdaHandler e passado através do contexto do serviço:

packages/api/src/handler.ts
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);
};

Acesse db do contexto nas suas implementações de operação:

packages/api/src/operations/list-users.ts
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 };
};

Executar o gerador novamente com um destino diferente adiciona o segundo banco de dados junto com o primeiro. Ambos os clientes são adicionados ao ServiceContext e instanciados em handler.ts:

packages/api/src/context.ts
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
myDb: Awaited<ReturnType<typeof getMyDb>>;
otherDb: Awaited<ReturnType<typeof getOtherDb>>;
}
packages/api/src/handler.ts
const myDb = await getMyDb();
const otherDb = await getOtherDb();
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
myDb,
otherDb,
});

Para permitir que sua API se conecte ao banco de dados em tempo de execução, as funções Lambda da API devem ser implantadas na mesma VPC que o banco de dados e receber acesso de rede e IAM.

No stack da sua aplicação, implante a API na mesma VPC que o banco de dados, depois chame allowDefaultPortFrom e grantConnect para abrir o caminho de rede e conceder permissão IAM rds-db:connect a cada manipulador 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);
});

Implante as funções Lambda da API em uma sub-rede privada com saída, não em uma sub-rede privada isolada. Em tempo de execução, getPrisma() recupera os detalhes de conexão do banco de dados do AWS AppConfig, que é um endpoint de serviço público da AWS que requer acesso à internet de saída.

Para conexões diretas ao cluster Aurora a partir de runtimes Lambda Node.js 20 ou posterior, carregue o pacote de CA do Amazon RDS definindo NODE_EXTRA_CA_CERTS:

packages/infra/src/stacks/application-stack.ts
const api = new Api(this, 'Api', {
integrations: Api.defaultIntegrations(this)
.withDefaultOptions({
environment: {
NODE_EXTRA_CA_CERTS: '/var/runtime/ca-cert.pem',
},
})
.build(),
});

Para mais detalhes, consulte a documentação do AWS Lambda sobre requisitos SSL/TLS para conexões Amazon RDS e a documentação TLS do Amazon RDS Proxy. Ao usar RDS Proxy, você não precisa configurar o pacote de CA do RDS na sua função Lambda.

O gerador aplica a mesma injeção de cliente Prisma dentro do manipulador de requisição em local-server.ts:

packages/api/src/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);
});
Terminal window
pnpm nx dev <api-project-name>

Isso inicia tanto a API quanto o banco de dados local. A variável de ambiente LOCAL_DEV=true é definida automaticamente, então o cliente Prisma se conecta ao banco de dados Docker local em vez do Aurora.