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ções possam acessar o banco de dados.

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

  1. Um projeto ts#smithy-api (backend TypeScript)
  2. Um projeto ts#rdb
  1. Instale o Nx Console VSCode Plugin se ainda não o fez
  2. Abra o console Nx no VSCode
  3. Clique em Generate (UI) na seção "Common Nx Commands"
  4. Procure por @aws/nx-plugin - connection
  5. Preencha os parâmetros obrigatórios
    • Clique em Generate

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

    Parâmetro Tipo Padrão Descrição
    sourceProject Obrigatório string - O projeto de origem
    targetProject Obrigatório string - 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.

    O gerador modifica três arquivos existentes no backend da sua 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 serve-local 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 implementações de suas operações:

    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 handler 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 egress, 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.

    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 serve-local <api-project-name>

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