eduardweb.
Prisma ORMIntermediar#prisma#ci-cd#database#node-js#testing

Cum scrii seed-uri Prisma pentru CI care nu crapă niciodată

De Florin Manea, 3 iul. 2026 · 17 vizualizări · 3 like-uri

Postat 3 iul. 2026
typescript
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
  // Forțăm Faker să genereze aceleași date la fiecare rulare în CI
  faker.seed(42);

  const roles = ['ADMIN', 'EDITOR', 'USER'];

  // 1. Seed pentru nomenclatoare folosind upsert
  for (const role of roles) {
    await prisma.role.upsert({
      where: { name: role },
      update: {}, // Nu modificăm nimic dacă rolul există deja
      create: { name: role },
    });
  }

  // 2. Utilizatori de test determiniști
  const testUsersCount = 5;
  for (let i = 0; i < testUsersCount; i++) {
    const email = `test-user-${i + 1}@eduardweb.ro`;
    
    await prisma.user.upsert({
      where: { email },
      update: {},
      create: {
        email,
        name: faker.person.fullName(),
        passwordHash: 'dummy-password-hash',
      },
    });
  }
}

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

Dacă și vouă vi s-a blocat pipeline-ul de CI din cauză că scriptul de seed a dat de o cheie duplicat sau a expirat vreun ID hardcodat, știți exact cât de frustrant e. Am trecut prin asta la un proiect cu peste 40 de tabele și vreo 12k de utilizatori activi, unde seed-ul rula automat la fiecare deploy de staging. Azi vă arăt cum facem seed-uri în Prisma care nu crapă în CI, folosind upsert-uri inteligente și date pseudo-aleatoare dar predictibile.

De ce crapă seed-urile clasice în CI?

Mulți începem simplu în Prisma: prisma.user.create({ data: ... }). Pe mașina locală merge perfect la prima rulare. A doua oară, când baza de date are deja date, primești o eroare de tipul 'Unique constraint failed'.

În CI, dacă rulezi teste de integrare pe o bază de date persistentă sau refolosită între build-uri, e rețeta perfectă pentru un build picat duminică noaptea. Soluția de bază este utilizarea upsert. În loc să arunci date în baza de date sperând să nu existe, definești clar ce înseamnă un record unic.

Trade-off-ul sincer: Upsert e sfânt, dar are un cost

Când folosești upsert, Prisma verifică dacă există înregistrarea după un câmp unic (de regulă id sau email). Dacă există, face update (chiar și un obiect gol {} dacă vrei să-l lași intact); dacă nu, face create.

Dar atenție la un aspect important: upsert este mult mai lent decât un createMany. Pentru 50 de înregistrări de nomenclator (roluri, categorii, țări), diferența e de milisecunde. Însă dacă încerci să generezi 3000 de tranzacții de test în CI folosind upsert individual, build-ul tău va dura cu 3-4 minute mai mult.

Am rezolvat problema asta separând seed-ul în două faze:

  1. Seed-ul de structură: Roluri, setări, chestii obligatorii ca aplicația să pornească. Aici folosim upsert exclusiv.
  2. Seed-ul de test (opțional): Date de volum pentru rularea testelor. Aici dăm un TRUNCATE pe tabelele respective și facem bulk insert rapid cu createMany.

Cum generăm date dinamice, dar predictibile, cu Faker

Dacă folosești @faker-js/faker ca să generezi nume sau emailuri în seed, riști ca testele din CI să devină instabile. O dată testul trece pentru că Faker a generat un nume scurt, altă dată pică pentru că a generat un string de 100 de caractere care îți strică layout-ul.

Trucul este să setezi un seed fix pentru motorul Faker: faker.seed(123). În felul ăsta, Faker va genera exact aceleași date la fiecare rulare a scriptului, asigurând predictibilitatea de care ai nevoie în teste.

Cum aveți configurat flow-ul de seed în CI? Curățați complet baza de date înainte de fiecare rulare sau vă bazați pe scripturi idempotente care fac update on conflict?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.