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 é:
- Ilegal sob a LGPD, pois reutiliza dados pessoais fora da finalidade original.
- Insegura, pois expõe PII em ambientes com controles de segurança menores.
- 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 →