Pular para o conteúdo

Banco de Dados Relacional

Filter this guide Pick generator option values to hide sections that don't apply.

Este gerador cria um novo projeto de banco de dados relacional baseado no Amazon Aurora (PostgreSQL ou MySQL) e Prisma ORM. Ele gera o código da aplicação e a infraestrutura necessária para provisionar e gerenciar um banco de dados usando AWS CDK ou Terraform, com definição de esquema declarativa, implantação automática de migrações e um cliente ORM com segurança de tipos.

Você pode gerar um novo projeto de banco de dados relacional de duas maneiras:

  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 - ts#rdb
  5. Preencha os parâmetros obrigatórios
    • Clique em Generate
    Parâmetro Tipo Padrão Descrição
    name Obrigatório string - Nome do projeto de banco de dados a ser gerado
    directory string packages O diretório onde armazenar a aplicação.
    subDirectory string - O subdiretório onde o projeto é colocado. Por padrão, este é o nome do projeto.
    service Obrigatório string Aurora Serviço de banco de dados relacional a ser provisionado.
    engine Obrigatório string PostgreSQL Motor de banco de dados a ser usado com o serviço selecionado.
    databaseUser string dbadmin Nome de usuário do administrador do banco de dados. O padrão é 'dbadmin'.
    databaseName string - Nome inicial do banco de dados. O padrão é o nome do projeto.
    ormFramework Obrigatório string Prisma Framework ORM a ser usado para o projeto gerado.
    iacProvider string Inherit O provedor IaC preferido. Por padrão, este é herdado da sua seleção inicial.

    O gerador criará a seguinte estrutura de projeto no diretório <directory>/<name>:

    • Directoryprisma
      • Directorymodels
        • example.prisma Definição de modelo de exemplo
      • schema.prisma Esquema principal do Prisma (referencia modelos)
    • Directoryscripts
      • docker-pull.ts Baixa a imagem Docker do banco de dados para desenvolvimento local
      • docker-start.ts Inicia um container de banco de dados local
      • wait-for-db.ts Aguarda o banco de dados local estar pronto
    • Directorysrc
      • index.ts Ponto de entrada do projeto
      • constants.ts Detalhes de conexão de desenvolvimento local e chave de configuração de runtime
      • prisma.ts Wrapper do cliente Prisma de runtime
      • utils.ts Auxiliares de configuração de runtime e secrets
      • create-db-user-handler.ts Manipulador Lambda usado para criar o usuário de banco de dados da aplicação durante a implantação
      • migration-handler.ts Manipulador Lambda usado para executar migrações de banco de dados durante a implantação
    • .gitignore Entradas do Git ignore incluindo a saída do cliente Prisma gerado
    • Dockerfile Definição de imagem de container para o manipulador de migração
    • project.json Configuração do projeto e alvos de build
    • prisma.config.ts Configuração para Prisma CLI

    Como este gerador fornece infraestrutura como código com base no iacProvider escolhido, ele criará um projeto em packages/common que inclui os constructs CDK ou módulos Terraform relevantes.

    O projeto comum de infraestrutura como código está estruturado da seguinte forma:

    • Directorypackages/common/constructs
      • Directorysrc
        • Directoryapp/ Constructs para infraestrutura específica de um projeto/gerador
        • Directorycore/ Constructs genéricos reutilizados pelos constructs em app
        • index.ts Ponto de entrada exportando os constructs de app
      • project.json Metas de build e configuração do projeto
    • Directorypackages/common/constructs/src
      • Directoryapp
        • Directorydbs
          • <name>.ts Infraestrutura específica para seu banco de dados
      • Directorycore
        • Directoryrdb
          • aurora.ts Construtor genérico de banco de dados Aurora

    O projeto gerado usa Prisma ORM para definir seu esquema de banco de dados e gerar um cliente com segurança de tipos. O fluxo de trabalho é model-first: adicione ou atualize arquivos de modelo Prisma no diretório prisma/models/ do seu projeto de banco de dados, depois gere uma migração a partir dessas alterações de modelo.

    Exemplo de modelo User:

    packages/postgres/prisma/models/user.prisma
    model User {
    id Int @id @default(autoincrement())
    firstName String
    lastName String
    }

    Para mais detalhes, consulte o guia oficial de modelagem de dados do Prisma.

    O gerador configura automaticamente o alvo generate para criar um cliente Prisma TypeScript com segurança de tipos sempre que você compilar o projeto. O cliente é escrito em generated/prisma (adicionado ao .gitignore).

    Você também pode gerar manualmente o cliente a qualquer momento:

    Terminal window
    pnpm nx generate <your-db-project-name>

    Use o alvo prisma para executar comandos da CLI do Prisma a partir da raiz do workspace:

    Terminal window
    pnpm nx run <project>:prisma generate

    O wrapper de runtime em src/prisma.ts exporta:

    • DB_PACKAGE_NAME - a chave usada sob o namespace de configuração de runtime database no AWS AppConfig
    • getPrisma() - carrega as configurações de conexão do banco de dados do AWS AppConfig e cria um cliente Prisma usando autenticação IAM

    O cliente automaticamente:

    • Recupera a configuração do banco de dados do AWS AppConfig usando a variável de ambiente RUNTIME_CONFIG_APP_ID
    • Gera tokens de autenticação temporários via AWS RDS Signer para autenticação IAM
    • Gerencia conexões SSL/TLS com validação de certificado
    • Manipula pooling de conexões através de pools de conexão de banco de dados persistentes

    Após adicionar ou atualizar modelos em prisma/models/, use migrate dev para gerar arquivos de migração e aplicá-los ao seu banco de dados local ao mesmo tempo.

    O alvo prisma gerado inicia automaticamente um banco de dados local via Docker antes de executar:

    Terminal window
    pnpm nx run <project>:prisma migrate dev

    Se você quiser apenas gerar os arquivos de migração sem aplicá-los ao banco de dados local, adicione --create-only:

    Terminal window
    pnpm nx run <project>:prisma migrate dev --create-only

    Isso gera uma nova pasta de migração em prisma/migrations cada vez que seu esquema muda:

    • Directoryprisma
      • Directorymigrations
        • Directory20260405013911_initial_migrations
          • migration.sql
        • migration_lock.toml
      • schema.prisma

    Quando você implanta a pilha AWS, a infraestrutura gerada aplica automaticamente as migrações geradas ao banco de dados implantado.

    Quando você baixa arquivos de migração criados por outros desenvolvedores, use migrate deploy para aplicar essas migrações existentes ao seu banco de dados local.

    Terminal window
    pnpm nx run <project>:prisma migrate deploy

    Neste fluxo de desenvolvimento local, migrate deploy aplica os arquivos de migração ao seu banco de dados local; ele não implanta o banco de dados na AWS.

    O alvo prisma gerado expõe a CLI do Prisma, então você pode usá-lo para executar qualquer comando suportado pelo Prisma contra o banco de dados local. Consulte a referência da CLI do Prisma para comandos disponíveis.

    Terminal window
    pnpm nx run <project>:prisma <prisma-command>

    Prisma Studio é um editor visual para seu banco de dados local. Use-o para navegar em tabelas, inspecionar e editar registros, filtrar dados, seguir relações e executar SQL bruto através do console SQL integrado. É útil para verificar migrações e semear dados de teste durante o desenvolvimento. Inicie-o com:

    Terminal window
    pnpm nx run <project>:prisma studio

    Embora esta seção descreva como conectar ao seu banco de dados a partir de uma API tRPC, ela também serve como referência para uso em qualquer outro projeto TypeScript.

    Importe getPrisma do seu pacote de banco de dados e chame-o dentro do seu manipulador para obter um cliente Prisma com segurança de tipos:

    packages/api/src/procedures/list-users.ts
    import { getPrisma } from ':my-scope/db';
    import { publicProcedure } from '../init.js';
    import { ListUsersOutputSchema } from '../schema/index.js';
    export const listUsers = publicProcedure
    .output(ListUsersOutputSchema)
    .query(async () => {
    const prisma = await getPrisma();
    return prisma.user.findMany({ orderBy: { id: 'asc' } });
    });

    getPrisma() retorna um cliente inicializado preguiçosamente e em cache. Chamadas subsequentes dentro do mesmo contexto de execução Lambda reutilizam o pool de conexões existente em vez de abrir um novo.

    O cliente Prisma expõe modelos totalmente tipados derivados do seu esquema prisma/models/, fornecendo segurança de tipos de ponta a ponta desde o banco de dados até a resposta da sua API.

    Em vez de chamar getPrisma() em cada procedimento, você também pode resolvê-lo uma vez em um middleware e anexá-lo ao contexto tRPC para que todos os procedimentos downstream possam acessá-lo diretamente.

    Primeiro, defina o plugin em src/middleware/db.ts seguindo o mesmo padrão do middleware gerado:

    packages/api/src/middleware/db.ts
    import { getPrisma } from ':my-scope/db';
    import { initTRPC } from '@trpc/server';
    export interface IDbContext {
    db: Awaited<ReturnType<typeof getPrisma>>;
    }
    export const createDbPlugin = () => {
    const t = initTRPC.context<IDbContext>().create();
    return t.procedure.use(async (opts) => {
    const db = await getPrisma();
    return opts.next({
    ctx: {
    ...opts.ctx,
    db,
    },
    });
    });
    };

    Em seguida, concatene-o a um procedimento base na sua inicialização tRPC:

    packages/api/src/init.ts
    import { createDbPlugin } from './middleware/db.js';
    export const dbProcedure = publicProcedure.concat(createDbPlugin());

    Procedimentos construídos em dbProcedure recebem db através do contexto sem precisar importar ou chamar getPrisma():

    packages/api/src/procedures/list-users.ts
    import { dbProcedure } from '../init.js';
    import { ListUsersOutputSchema } from '../schema/index.js';
    export const listUsers = dbProcedure
    .output(ListUsersOutputSchema)
    .query(async ({ ctx: { db } }) => {
    return db.user.findMany({ orderBy: { id: 'asc' } });
    });

    O gerador de banco de dados relacional cria infraestrutura CDK ou Terraform com base no seu iacProvider selecionado.

    O construtor CDK é criado em common/constructs. Exemplo de uso:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    export class ApplicationStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    ...
    const db = new MyDatabase(this, 'Db', {
    vpc,
    vpcSubnets: {
    subnetType: SubnetType.PRIVATE_ISOLATED,
    }
    });
    }
    }

    Isso provisiona um cluster Aurora com RDS Proxy, credenciais de administrador, usuário de banco de dados da aplicação, registro de configuração de runtime e manipulador de migração.

    A infraestrutura gerada cria dois usuários de banco de dados:

    • Usuário administrador - Criado durante o provisionamento do cluster com credenciais armazenadas no AWS Secrets Manager
    • Usuário da aplicação - Criado via um recurso personalizado Lambda com autenticação IAM habilitada e privilégios completos no banco de dados da aplicação

    O usuário da aplicação é criado automaticamente com um nome aleatório e autenticação IAM. getPrisma() já está configurado para autenticar como este usuário usando tokens RDS de curta duração, então o código da sua aplicação nunca manipula senhas de banco de dados.

    Sua VPC deve incluir sub-redes públicas, sub-redes privadas com egress e sub-redes privadas isoladas. O banco de dados pode ser executado em sub-redes privadas isoladas, enquanto as funções Lambda da API devem ser executadas em sub-redes privadas com egress para que possam alcançar serviços AWS como AppConfig.

    packages/infra/src/stacks/application-stack.ts
    const vpc = new Vpc(this, 'Vpc', {
    subnetConfiguration: [
    {
    name: 'public',
    subnetType: SubnetType.PUBLIC,
    },
    {
    name: 'private_with_egress',
    subnetType: SubnetType.PRIVATE_WITH_EGRESS,
    },
    {
    name: 'private_isolated',
    subnetType: SubnetType.PRIVATE_ISOLATED,
    },
    ],
    });

    Na sua pilha de 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 Api(this, 'Api', {
    integrations: Api.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 (recomendado) ou uma sub-rede pública, não 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 AWS público. Funções Lambda em uma sub-rede privada isolada não têm acesso de saída à internet e não podem alcançar o AppConfig. Sub-redes privadas com egress roteiam o tráfego de saída através de um NAT Gateway que fica em uma sub-rede pública.

    A infraestrutura gerada inclui um RDS Proxy por padrão, que fica entre sua aplicação e o cluster Aurora. O RDS Proxy oferece vários benefícios:

    • Pooling de conexões - Mantém um pool de conexões de banco de dados que podem ser compartilhadas entre instâncias da aplicação, reduzindo a sobrecarga de estabelecer novas conexões
    • Resiliência de conexão - Manipula automaticamente failovers e reconexões durante substituições de instâncias Aurora ou manutenção
    • Autenticação IAM - Suporta autenticação de banco de dados baseada em IAM, eliminando a necessidade de gerenciar credenciais de banco de dados no código da sua aplicação
    • Segurança aprimorada - Impõe criptografia TLS para todas as conexões

    Você pode desabilitar o proxy RDS da seguinte forma:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    enableRdsProxy: false,
    });

    Quando o RDS Proxy está desabilitado, sua aplicação se conecta diretamente ao endpoint do cluster Aurora.

    Para conexões diretas ao cluster Aurora a partir de runtimes Lambda Node.js 20 ou posterior, configure a função Lambda para carregar o pacote de CA do Amazon RDS:

    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 os requisitos SSL/TLS do AWS Lambda 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.

    A infraestrutura gerada pode ser personalizada para corresponder aos requisitos da sua carga de trabalho. Os exemplos a seguir demonstram algumas opções de personalização comuns disponíveis.

    Configure as instâncias writer e reader para seu cluster Aurora.

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    writer: ClusterInstance.serverlessV2('writer'),
    readers: [ClusterInstance.serverlessV2('reader')],
    });

    Controle os limites de escalonamento do Aurora Serverless v2 para corresponder à sua carga de trabalho.

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    serverlessV2MinCapacity: 0.5,
    serverlessV2MaxCapacity: 8,
    });

    Fixe uma versão específica do engine Aurora.

    Por padrão, a imagem Docker do banco de dados local gerada corresponde à versão padrão do engine Aurora. Se você alterar a versão do engine Aurora, é recomendado também usar uma versão de banco de dados Docker local correspondente para máxima compatibilidade. Consulte as notas de lançamento da AWS para versões do Aurora PostgreSQL e versões do Aurora MySQL para identificar a versão correspondente do banco de dados da comunidade.

    A imagem do banco de dados local é configurada no alvo serve-local do projeto de banco de dados gerado em project.json. Atualize o argumento de imagem passado para scripts/docker-start.ts quando você alterar as versões do engine.

    engine = PostgreSQL
    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    engineVersion: AuroraPostgresEngineVersion.VER_17_7,
    });
    engine = MySQL
    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    engineVersion: AuroraMysqlEngineVersion.VER_3_12_0,
    });

    A proteção contra exclusão está habilitada por padrão (deletionProtection: true no CDK, deletion_protection = true no Terraform) para proteger o cluster Aurora contra exclusão acidental.

    Você pode desabilitar a proteção contra exclusão para ambientes onde a exclusão do banco de dados é esperada, como pilhas de desenvolvimento ou preview de curta duração.

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    deletionProtection: false,
    });

    O construtor CDK retém o cluster Aurora por padrão (removalPolicy: RemovalPolicy.RETAIN). Altere isso quando você quiser que a exclusão da pilha CDK faça snapshot ou destrua o cluster.

    Ao usar RemovalPolicy.DESTROY, a proteção contra exclusão também deve ser desabilitada antes que o cluster possa ser excluído.

    packages/infra/src/stacks/application-stack.ts
    import { RemovalPolicy } from 'aws-cdk-lib';
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    removalPolicy: RemovalPolicy.SNAPSHOT,
    });

    Para um ambiente efêmero onde o banco de dados deve ser excluído com a pilha:

    packages/infra/src/stacks/application-stack.ts
    import { RemovalPolicy } from 'aws-cdk-lib';
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    deletionProtection: false,
    removalPolicy: RemovalPolicy.DESTROY,
    });

    A chave KMS usada para criptografar o cluster Aurora e seu secret de credenciais tem rotação automática de chave habilitada por padrão. Desabilite-a se sua política de segurança gerenciar a rotação externamente.

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', {
    ...
    enableKeyRotation: false,
    });
    engine = MySQL

    Ao usar Aurora MySQL com respostas de streaming do API Gateway (por exemplo, com httpBatchStreamLink do tRPC), o cliente MySQL do Prisma mantém o event loop do Node.js após a conclusão de uma consulta, impedindo que o Lambda libere o stream e encerre a requisição.

    Para contornar isso, desconecte explicitamente o cliente em um bloco finally após cada consulta para que o event loop fique livre para sair e a resposta de streaming possa ser concluída.

    Opção 1: por procedimento

    export const listExampleTable = publicProcedure
    .output(z.array(ExampleTableSchema))
    .query(async () => {
    const prisma = await getPrisma();
    try {
    return await prisma.exampleTable.findMany();
    } finally {
    await prisma.$disconnect();
    }
    });

    Opção 2: middleware tRPC

    Se você estiver usando o padrão de middleware, adicione a chamada $disconnect() ao middleware para que todos os procedimentos construídos sobre ele sejam cobertos automaticamente:

    packages/api/src/middleware/db.ts
    import { getPrisma } from ':my-scope/db';
    import { initTRPC } from '@trpc/server';
    export interface IDbContext {
    db: Awaited<ReturnType<typeof getPrisma>>;
    }
    export const createDbPlugin = () => {
    const t = initTRPC.context<IDbContext>().create();
    return t.procedure.use(async (opts) => {
    const db = await getPrisma();
    try {
    return await opts.next({
    ctx: {
    ...opts.ctx,
    db,
    },
    });
    } finally {
    await db.$disconnect();
    }
    });
    };

    Tokens de autenticação IAM do RDS expiram após 15 minutos. O cliente MySQL do Prisma captura o token IAM como um valor estático no momento em que getPrisma() é chamado. Uma conexão aberta existente não é afetada, mas se uma nova conexão precisar ser estabelecida após o token ter expirado, a autenticação falhará. O adaptador PostgreSQL evita isso atualizando o token dinamicamente cada vez que o pool abre uma nova conexão, mas o adaptador MySQL não tem mecanismo equivalente.

    Para tarefas de longa duração, como jobs em lote ou migrações de dados, chame getPrisma() no início de cada unidade de trabalho em vez de uma vez para toda a operação. Como getPrisma() sempre cria um cliente novo e busca um novo token IAM para MySQL, isso garante que cada conexão autentique com um token válido.