PrismaFaker.jsSeedNode.jsLGPD

Seed de Banco de Dados com Dados Sintéticos usando Prisma e Faker.js

Como popular ambientes de desenvolvimento e staging com dados realistas e em conformidade com a LGPD usando Prisma ORM, Faker.js e geradores de CPF/CNPJ.

5 de abril de 2025 · 5 min de leitura

O problema do seed com dados reais

É comum encontrar scripts de seed que simplesmente copiam registros do banco de produção ou usam CPFs, e-mails e nomes de pessoas reais coletados "por conveniência". Essa prática é:

  1. Ilegal sob a LGPD, pois reutiliza dados pessoais fora da finalidade original.
  2. Insegura, pois expõe PII em ambientes com controles de segurança menores.
  3. Frágil, pois os dados de produção mudam e o seed quebra.

A alternativa é criar um pipeline de seed 100% sintético, que gere dados realistas, estruturalmente corretos e sem vínculo com pessoas reais.


Configurando o ambiente

Dependências necessárias

npm install --save-dev @faker-js/faker
npm install @prisma/client
npx prisma init

Schema Prisma de exemplo

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(cuid())
  name      String
  email     String   @unique
  cpf       String   @unique
  birthDate DateTime
  state     String
  createdAt DateTime @default(now())
  orders    Order[]
}

model Order {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  total     Decimal  @db.Decimal(10, 2)
  status    String
  createdAt DateTime @default(now())
}

Implementando o gerador de CPF para seed

Em vez de depender de uma biblioteca externa, use a implementação nativa:

// prisma/lib/cpf-generator.ts

const UF_DIGITS: Record<string, number> = {
  RS: 0, DF: 1, GO: 1, MS: 1, MT: 1, TO: 1,
  AC: 2, AM: 2, AP: 2, PA: 2, RO: 2, RR: 2,
  CE: 3, MA: 3, PI: 3,
  AL: 4, PB: 4, PE: 4, RN: 4,
  BA: 5, SE: 5,
  MG: 6,
  ES: 7, RJ: 7,
  SP: 8,
  PR: 9, SC: 9,
};

function randomDigit(): number {
  return Math.floor(Math.random() * 10);
}

function calcVerifier(digits: number[], weights: number[]): number {
  const sum = digits.reduce((acc, d, i) => acc + d * weights[i], 0);
  const rem = sum % 11;
  return rem < 2 ? 0 : 11 - rem;
}

export function generateCpf(uf?: string): string {
  const base = Array.from({ length: 8 }, randomDigit);
  const regional = uf ? (UF_DIGITS[uf] ?? randomDigit()) : randomDigit();
  base.push(regional);

  const v1 = calcVerifier(base, [10, 9, 8, 7, 6, 5, 4, 3, 2]);
  base.push(v1);
  const v2 = calcVerifier(base, [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]);
  base.push(v2);

  return base.join("");
}

export function formatCpf(cpf: string): string {
  return cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
}

Script de seed completo

// prisma/seed.ts
import { PrismaClient } from "@prisma/client";
import { faker } from "@faker-js/faker/locale/pt_BR";
import { generateCpf, formatCpf } from "./lib/cpf-generator";

const prisma = new PrismaClient();

const STATES = [
  "AC","AL","AM","AP","BA","CE","DF","ES","GO","MA","MG",
  "MS","MT","PA","PB","PE","PI","PR","RJ","RN","RO","RR",
  "RS","SC","SE","SP","TO",
];

const ORDER_STATUSES = ["pending", "processing", "shipped", "delivered", "cancelled"];

async function seedUsers(count: number) {
  const cpfsGenerated = new Set<string>();
  const emailsGenerated = new Set<string>();
  const users = [];

  while (users.length < count) {
    const state = faker.helpers.arrayElement(STATES);
    const cpf = generateCpf(state);
    const email = faker.internet.email().toLowerCase();

    // Garante unicidade
    if (cpfsGenerated.has(cpf) || emailsGenerated.has(email)) continue;
    cpfsGenerated.add(cpf);
    emailsGenerated.add(email);

    users.push({
      name: faker.person.fullName(),
      email,
      cpf: formatCpf(cpf),
      birthDate: faker.date.birthdate({ min: 18, max: 75, mode: "age" }),
      state,
    });
  }

  await prisma.user.createMany({ data: users, skipDuplicates: true });
  console.log(`✓ ${users.length} usuários criados`);
  return await prisma.user.findMany({ select: { id: true } });
}

async function seedOrders(userIds: { id: string }[], ordersPerUser: number) {
  const orders = userIds.flatMap(({ id: userId }) =>
    Array.from({ length: faker.number.int({ min: 1, max: ordersPerUser }) }, () => ({
      userId,
      total: faker.number.float({ min: 29.9, max: 4999.9, fractionDigits: 2 }),
      status: faker.helpers.arrayElement(ORDER_STATUSES),
      createdAt: faker.date.between({
        from: new Date("2024-01-01"),
        to: new Date(),
      }),
    }))
  );

  await prisma.order.createMany({ data: orders });
  console.log(`✓ ${orders.length} pedidos criados`);
}

async function main() {
  console.log("🌱 Iniciando seed com dados sintéticos...\n");

  await prisma.order.deleteMany();
  await prisma.user.deleteMany();

  const userIds = await seedUsers(200);
  await seedOrders(userIds, 5);

  console.log("\n✅ Seed concluído com sucesso!");
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());

Configuração no package.json

{
  "prisma": {
    "seed": "tsx prisma/seed.ts"
  }
}

Execute com:

npx prisma db seed

Estratégias avançadas de seed

1. Seed determinístico com seed de aleatoriedade fixo

Para garantir que o seed produza sempre os mesmos dados (útil para snapshots de testes):

import { faker } from "@faker-js/faker/locale/pt_BR";

faker.seed(42); // Qualquer número fixo — garante reprodutibilidade

2. Separação por ambiente

// prisma/seed.ts
const ENV = process.env.NODE_ENV ?? "development";

const CONFIG = {
  development: { users: 50, ordersPerUser: 3 },
  staging: { users: 500, ordersPerUser: 10 },
  test: { users: 10, ordersPerUser: 2 },
};

const { users, ordersPerUser } = CONFIG[ENV as keyof typeof CONFIG] ?? CONFIG.development;

3. Seed incremental (sem apagar dados existentes)

async function seedUsers(count: number) {
  const existing = await prisma.user.count();
  const needed = Math.max(0, count - existing);

  if (needed === 0) {
    console.log("ℹ️  Usuários já existentes suficientes, pulando...");
    return;
  }

  // ... gera apenas `needed` novos usuários
}

Integrando ao pipeline de CI/CD

# .github/workflows/test.yml
- name: Setup database
  run: |
    npx prisma migrate reset --force --skip-seed
    npx prisma db seed
  env:
    DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
    NODE_ENV: test

Conclusão

Um pipeline de seed com dados sintéticos é um investimento de poucas horas que traz retornos contínuos: ambientes de desenvolvimento realistas, conformidade com a LGPD e testes mais confiáveis. O Prisma facilita a orquestração, e combinado com Faker.js e geradores de CPF como o deste site, você tem tudo o que precisa para nunca mais precisar de dados reais em ambientes de teste.

Precisa gerar CPFs para testar sua implementação?

Usar o Gerador de CPF →