npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

prisma-seed

v1.1.0

Published

Versioned, reversible database seeding for Prisma projects: run-once seeders, batches and rollback, with Laravel-style commands.

Readme

prisma-seed

Versioned, reversible database seeding for Prisma projects — with Laravel-style commands.

Prisma gives you one prisma/seed.ts that reruns from scratch every time. prisma-seed gives you migration-like seeders: each file runs once, is recorded in a ledger table, is grouped into a batch, and can be rolled back.

npm install --save-dev prisma-seed
npx prisma-seed generate User   # scaffold prisma/seeders/<timestamp>_User.[ts|js]
npx prisma-seed run             # apply what's pending
npx prisma-seed status          # see what's applied and what isn't
npx prisma-seed rollback        # revert the last batch

The client is injected, never constructed by the library. That is what makes it work with PrismaClient and, equally, with any other client that satisfies a small contract — including the ZenStackClient from ZenStack v3.


Features

  • Run-once semantics. Every seeder is recorded in a ledger table (SeedExecution by default). Running twice does nothing the second time.
  • Batches. Each run groups its seeders into a numbered batch, so rollback can revert exactly the last one.
  • Reversible. Each seeder exports down(); rollback, refresh and rollback --all use it.
  • Atomic. Each seeder and its ledger record run inside one transaction. A failure halfway through leaves neither the data nor the record.
  • Client injection. runSeeders(client) — the library never calls new PrismaClient() when you give it a client, and never disconnects one it did not create.
  • Four SQL dialects. PostgreSQL, MySQL, SQLite and SQL Server, each with its own DDL, identifier quoting and placeholder syntax.
  • CLI and programmatic API. Six commands, plus runSeeders / rollbackSeeders for a seed.ts of your own.
  • One command everywhere. npx prisma-seed <command> works the same in any project — JavaScript or TypeScript, CommonJS or ESM, NestJS, Next.js or anything else. TypeScript configuration, seeders and client modules load without a loader to install, tsconfig paths aliases included.
  • Scaffolding. generate writes a typed seeder and picks TypeScript, ESM or CommonJS based on your project.
  • fresh. Truncates every table and resets autoincrement counters — the reset you want between test runs.
  • Automatic ledger migration. A ledger table from an older layout gains its batch column on first run, with the existing history placed in batch 1.

Requirements

| | | |---|---| | Node.js | >= 22.12.0. Enforced by engines. The floor comes from commander@15, a runtime dependency; on Node 18 the CLI does not start at all. | | A database client | Either @prisma/client, or any object satisfying the SeedClient contract. | | Databases | PostgreSQL, MySQL, SQLite or SQL Server. MongoDB is not supported. | | TypeScript | Optional, and nothing to set up. The CLI loads TypeScript configuration and seeders by itself (see TypeScript), and the package ships its own type declarations. JavaScript projects work without any TypeScript at all. |

@prisma/client is an optional peer dependency (>=5). Install it if you want the CLI to build the client for you; a project that injects its own client does not need it, and npm will not pull it in.

The range has no upper bound on purpose. With npm, an installed @prisma/client outside the declared range is not a warning but an ERESOLVE error that blocks the install, and the library only talks to the client through the SeedClient contract: a new Prisma major should not stop you from injecting its client.

Installation

npm install --save-dev prisma-seed

Nothing else is required for a standard Prisma 5 or 6 project: the CLI resolves @prisma/client from your project and builds the client itself. Prisma 7 needs one more step — see Prisma 7.

Commands

npx prisma-seed generate Tarifas            # create the seeder file
npx prisma-seed run                         # apply what's pending
npx prisma-seed run --dry-run               # show what would run, touch nothing
npx prisma-seed run Roles                   # one seeder, short name
npx prisma-seed run 20260822102358_Roles    # one seeder, full name
npx prisma-seed status                      # applied / pending
npx prisma-seed rollback                    # revert the last batch
npx prisma-seed fresh --seed                # empty everything and re-seed

Variants:

npx prisma-seed fresh --seed --force        # no prompt (CI, scripts)
npx prisma-seed fresh --seed --dry-run      # see what it would do, change nothing
npx prisma-seed refresh                     # surgical: only redo your seeders

Reference

| Command | What it does | |---|---| | generate <name> | Scaffolds a new seeder in seedersDir. Does not touch the database. | | run [name] | Applies every pending seeder, or only the one named. Groups them into a new batch. | | status | Lists every seeder as applied, pending, or recorded-but-missing. The only command that writes nothing — not even the ledger table. | | rollback [name] | Reverts the last batch by calling each seeder's down(). With a name, reverts just that one. | | refresh | Reverts everything via down(), then re-applies it all. Leaves data the seeders never touched alone. | | fresh | Truncates every table and resets autoincrement counters. With --seed, re-applies the seeders afterwards. |

Options

| Command | Option | What it does | |---|---|---| | generate | --model <model> | Target model, when it differs from the seeder name | | | --ts | Force TypeScript, even if the project does not look like it | | | --js | Force JavaScript, even if the project is TypeScript | | run | --class <name> | Alias of the positional name, for familiarity with Laravel | | | --step <n> | Apply only the next N pending seeders | | | --dry-run | List what would run, without touching the database | | | --force | Allow running with NODE_ENV=production | | rollback | --step <n> | Revert the last N seeders | | | --all | Revert the whole history, not just the last batch | | | --dry-run | List what would be reverted | | | --force | Allow running with NODE_ENV=production | | refresh | --dry-run | List what would be reverted and re-applied | | | --force | Allow running with NODE_ENV=production | | fresh | --seed | Apply the seeders after emptying | | | --dry-run | List the tables it would empty | | | --force | Skip the confirmation prompt, and allow NODE_ENV=production |

Global options, valid before any command:

| Option | What it does | |---|---| | -v, --version | Print the installed version | | -h, --help | Help for the program or for a command | | -q, --quiet | Only show errors | | --verbose | Show every step: resolved root, engine, schema, client capabilities | | --cwd <path> | Project root. By default it is found by walking up from the current directory | | --no-transaction | Do not wrap each seeder in a transaction |

Exit codes: 0 success · 1 a seeder or the operation failed · 2 usage error (bad argument, ambiguous name) · 3 the client could not be resolved or connected.

As npm scripts

The same scripts in every project, whatever its stack:

{
  "scripts": {
    "seed": "prisma-seed run",
    "seed:new": "prisma-seed generate",
    "seed:status": "prisma-seed status",
    "seed:rollback": "prisma-seed rollback",
    "seed:refresh": "prisma-seed refresh",
    "seed:fresh": "prisma-seed fresh --seed"
  }
}

Then npm run seed, and -- to pass arguments through npm:

npm run seed:new -- Tarifas
npm run seed -- --dry-run
npm run seed -- Roles
npm run seed:fresh -- --force

PrismaClient

The default path. In a project with prisma/schema.prisma and a generated client, there is nothing to configure. That holds for Prisma 5 and 6; Prisma 7 builds its client with a driver adapter the library cannot choose for you, so there you inject it.

CLI

npx prisma-seed generate User
npx prisma-seed run

The CLI finds your project root, reads the provider from prisma/schema.prisma, constructs a PrismaClient, creates the ledger table if needed, applies pending seeders, and disconnects.

Programmatic

import { PrismaClient } from '@prisma/client';
import { runSeeders } from 'prisma-seed';

const prisma = new PrismaClient();

const report = await runSeeders(prisma);
console.log(report); // { executed: [...], skipped: [...], batch: 1, dryRun: false }

await prisma.$disconnect(); // you opened it, you close it

runSeeders and rollbackSeeders never disconnect a client you passed in. The CLI does close the one it creates, because there it owns the process.

Prisma 7

Prisma 7 builds its client with a driver adapter, and its default generator (prisma-client) writes that client to the output directory of your schema rather than into @prisma/client. The library cannot guess either of the two, so the automatic path does not apply: inject the client. Everything else — the ledger, batches, transactions, rollback — works unchanged.

Support status. Verified with @prisma/client 7.10, injected through seeder.config.ts, on PostgreSQL 16 (@prisma/adapter-pg), MySQL 8 (@prisma/adapter-mariadb) and SQLite (@prisma/adapter-better-sqlite3): run, status, rollback, refresh and fresh --seed, starting from a database that has no ledger table yet.

Providing the client

Build it the way your application does:

// seeder.config.ts
import 'dotenv/config'; // only if DATABASE_URL comes from a .env file
import { PrismaPg } from '@prisma/adapter-pg';
import { defineConfig } from 'prisma-seed';
import { PrismaClient } from './generated/prisma/client'; // your generator's `output`

export default defineConfig({
  // A factory, so the pool is opened only by commands that need the database.
  client: () =>
    new PrismaClient({
      adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
    }),

  // Types the seeders that `generate` writes. The path is relative to the
  // seeders directory, `prisma/seeders` by default.
  clientType: {
    import: "import type { PrismaClient } from '../../generated/prisma/client'",
    type: 'PrismaClient',
  },
});

For MySQL or MariaDB the adapter is PrismaMariaDb from @prisma/adapter-mariadb; for SQLite, PrismaBetterSqlite3 from @prisma/adapter-better-sqlite3.

The configuration and the generated client being TypeScript changes nothing: the commands are the usual ones, and so are the npm scripts.

npm install --save-dev prisma-seed
npx prisma-seed generate User
npx prisma-seed run

From code, pass the client you already have: await runSeeders(prisma).

If you forget to inject it

The CLI stops with exit code 3 and says what to do. With the prisma-client generator:

✗ No se pudo cargar "@prisma/client" desde /app: Cannot find module '.prisma/client/default'
  Con Prisma 7, inyecta tu cliente ya construido con su driver adapter en la clave "client" de seeder.config (seccion "Prisma 7" del README). Con Prisma 5/6, genera el cliente con: npx prisma generate

With the legacy prisma-client-js generator, which Prisma 7 still ships, the message is No se pudo construir PrismaClient en ...: PrismaClient was instantiated without any options. A driver adapter is required ..., with the same hint.

ZenStack v3

ZenStack v3 replaced Prisma's ORM with its own, so a ZenStack project may not have @prisma/client at all. prisma-seed supports it because it depends on a contract rather than on an ORM: ZenStackClient exposes $queryRawUnsafe, $executeRawUnsafe, $transaction, $connect and $disconnect with the same shapes the library needs.

Support status. ZenStack v3 is supported. Compatibility has been verified against @zenstackhq/[email protected] on PostgreSQL, in a Next.js 16 application. Next.js is not a requirement of prisma-seed — it is simply the application the verification was performed in. Other frameworks and other databases are expected to work but have not been verified.

Providing the client

ZenStack's client is built with a Kysely dialect and a driver, so the library cannot construct it for you. Declare it once:

// seeder.config.mjs
export default {
  // The application's own client. Returned lazily so the pool is not opened
  // just to read the configuration.
  client: async () => (await import('./lib/db.ts')).db,

  // ZenStack projects usually have no `prisma/` directory.
  seedersDir: 'zenstack/seeders',
};

Or inject it directly:

import { runSeeders } from 'prisma-seed';
import { db } from './lib/db';

await runSeeders(db);

The engine is read from the datasource block, which ZModel declares the same way Prisma does. zenstack/schema.zmodel is one of the locations searched automatically, including the single-quoted provider = 'postgresql' form that ZModel examples use.

Two things to know

Use the client without access policies. The ledger is maintained with raw SQL, and ZenStack's policy plugin rejects raw SQL by default. Inject the plain client — a seed is system work, not an action on behalf of a user:

// lib/db.ts
export const db = new ZenStackClient(schema, { dialect });  // ← inject this one
export const authDb = db.$use(new PolicyPlugin());          // ← not this one

If you inject a policy-enforcing client, the error message says so.

transactionTimeout does not apply. ZenStack's $transaction accepts options, but only { isolationLevel }. The library always sends the same options object and each client applies what it understands, so the setting is simply ignored there.

NestJS

A NestJS application builds its PrismaClient inside a provider (PrismaService), but the CLI runs outside Nest's dependency injection. So seeding gets its own client in seeder.config.ts, with the same driver adapter and connection string. Nothing else about the application changes.

Support status. Verified with NestJS 12.1 (@nestjs/cli 12.0), Prisma 7.10 with the prisma-client generator writing to src/generated/prisma, @prisma/adapter-pg and PostgreSQL 16, in a CommonJS project whose tsconfig.build.json sets rootDir: "./src". NestJS is not a dependency of prisma-seed.

1. Install

npm install --save-dev prisma-seed

Nothing else: the CLI loads seeder.config.ts, the generated client and the seeders — all of them TypeScript — by itself.

2. Create seeder.config.ts at the project root

import 'dotenv/config'; // only if DATABASE_URL comes from a .env file
import { PrismaPg } from '@prisma/adapter-pg';
import { defineConfig } from 'prisma-seed';
import { PrismaClient } from './src/generated/prisma/client';

export default defineConfig({
  // The same client PrismaService builds, without Nest around it.
  client: () =>
    new PrismaClient({
      adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
    }),

  // Seeders written by `generate` are typed with your generated client.
  clientType: {
    import: "import type { PrismaClient } from '../../src/generated/prisma/client'",
    type: 'PrismaClient',
  },
});

It does not import PrismaService on purpose: that is a provider that expects Nest to inject its dependencies (ConfigService, for instance) and to drive its lifecycle. A plain client has neither requirement.

3. Keep the seeders out of nest build

Add the config file and the seeders directory to the exclude of tsconfig.build.json, before the next build:

// tsconfig.build.json — keep your current options; add the last two entries
{
  "exclude": [
    "node_modules",
    "test",
    "dist",
    "**/*spec.ts",
    "seeder.config.ts",
    "prisma/seeders"
  ]
}

They are not part of the application, and with rootDir: "./src" leaving them in makes nest build fail:

error TS6059: File '/app/seeder.config.ts' is not under 'rootDir' '/app/src'. 'rootDir' is expected to contain all source files.

The failed build still writes JavaScript outside dist/: a seeder.config.js at the root and a .js next to every seeder (plus .d.ts and .js.map, with Nest's default declaration and sourceMap). That .js has the same name as your .ts, and it is a stale copy that can run instead of it. If a build already failed that way, delete those files.

Leave tsconfig.json alone: when it covers the whole project, as Nest's does by default, your editor keeps type-checking the seeders against the generated client.

4. Use the same commands as any project

npx prisma-seed <command> directly, or the usual npm scripts — identical to the ones of a Next.js or any other project:

{
  "scripts": {
    "seed": "prisma-seed run",
    "seed:new": "prisma-seed generate",
    "seed:status": "prisma-seed status",
    "seed:rollback": "prisma-seed rollback",
    "seed:refresh": "prisma-seed refresh",
    "seed:fresh": "prisma-seed fresh --seed"
  }
}
npx prisma-seed generate User   # or: npm run seed:new -- User
npm run seed                    # applies what is pending; creates the ledger the first time
npm run seed:status
npm run seed:rollback
npm run seed:fresh              # development: empties every table, then seeds again

seed:fresh is the migrate:fresh --seed of Laravel: it empties every table, resets the autoincrement counters and applies all the seeders again. It asks for confirmation, and refuses to run with NODE_ENV=production unless you pass --force.

Things to know

  • Docker Compose. Run the scripts inside the API container, where DATABASE_URL is already set: docker compose exec api npm run seed. dotenv does not override a variable that is already defined.
  • prisma migrate dev. The ledger table is created with raw SQL, so the migration history does not know it: the next migrate dev reports Drift detected ... [+] Added tables - SeedExecution and asks to reset the database. Declare the ledger model before the first seed and let a migration create it; the library then finds the table and leaves it alone.
  • Production. prisma-seed is a dev dependency, so an image built with npm ci --omit=dev does not have it. And run, rollback, refresh and fresh refuse to run with NODE_ENV=production unless you pass --force.
  • Coming from 1.0. Scripts of the form tsx node_modules/prisma-seed/dist/cli.js <command> keep working, but are no longer needed: replace them with prisma-seed <command>.

Usage

1. Create a seeder

npx prisma-seed generate User

This writes prisma/seeders/20260101120000_User.ts — or .js, depending on what it detects. The timestamp prefix is what gives the run order.

2. Fill it in

import type { PrismaClient } from '@prisma/client';
import type { SeedContext } from 'prisma-seed';

type Ctx = SeedContext<PrismaClient>;

export async function main({ prisma, logger }: Ctx) {
  await prisma.user.create({
    data: { email: '[email protected]', name: 'Ada Lovelace' },
  });
  logger.info('Ada created');
}

export async function down({ prisma }: Ctx) {
  await prisma.user.deleteMany({ where: { email: '[email protected]' } });
}

main is required. down is optional, but without it rollback will skip the seeder and say so.

Seeders do not need to be idempotent: the ledger guarantees a single execution. create is fine; upsert is not required.

Never construct a client inside a seeder — that is one connection pool per file. It arrives in the context.

3. Run

npx prisma-seed run
npx prisma-seed status
✓ ejecutado     1  20260101120000_User
· pendiente        20260101130000_Post

2 en disco · 1 ejecutados · 1 pendientes

CLI output and error messages are currently in Spanish.

4. Undo

npx prisma-seed rollback          # the last batch
npx prisma-seed rollback User     # one seeder
npx prisma-seed rollback --all    # everything

Execution order

Seeders run in filename order, computed as:

  1. Files with a YYYYMMDDHHmmss_ prefix first, oldest first.
  2. Files without a prefix after them, alphabetically.
  3. Ties broken alphabetically.

There is no dependency resolution between seeders. If Post needs User, give User an earlier timestamp.

Naming a single seeder

run, rollback and --class accept a full name or a shorthand. Resolution is tried in this order: exact match, case-insensitive match, match ignoring the timestamp prefix, then partial match. An ambiguous name is an error listing the candidates, never a silent guess.

npx prisma-seed run 20260101120000_User
npx prisma-seed run User
npx prisma-seed run user

An already-applied seeder is not re-run. To repeat one:

npx prisma-seed rollback User && npx prisma-seed run User

API

The command line is documented in Commands. This section covers the programmatic API — what import ... from 'prisma-seed' gives you.

runSeeders(client, options?)

function runSeeders(client: SeedClient, options?: RunSeedersOptions): Promise<RunReport>;

interface RunSeedersOptions {
  cwd?: string;               // project root; found by walking up if omitted
  config?: UserSeederConfig;  // overrides for any configuration key
  logger?: Logger;            // defaults to the library's console logger
  ensureLedgerTable?: boolean; // default true
  only?: string;              // run just this seeder
  step?: number;              // run at most N pending seeders
  dryRun?: boolean;           // report without touching the database
}

interface RunReport {
  executed: string[];
  skipped: { name: string; reason: string }[];
  batch: number;
  dryRun: boolean;
}

rollbackSeeders(client, options?)

function rollbackSeeders(
  client: SeedClient,
  options?: RollbackSeedersOptions
): Promise<RollbackReport>;

interface RollbackSeedersOptions {
  cwd?: string;
  config?: UserSeederConfig;
  logger?: Logger;
  ensureLedgerTable?: boolean;
  only?: string;    // revert just this one
  step?: number;    // revert the last N
  all?: boolean;    // revert the whole history
  dryRun?: boolean;
}

interface RollbackReport {
  reverted: string[];
  skipped: { name: string; reason: string }[];
  dryRun: boolean;
}

defineConfig(config)

Identity helper that types a seeder.config.* file. Returns its argument unchanged.

SeedClient

The contract. Only the two raw-SQL methods are required — they are what maintains the ledger:

interface RawSqlClient {
  $queryRawUnsafe<T = unknown>(query: string, ...values: unknown[]): Promise<T>;
  $executeRawUnsafe(query: string, ...values: unknown[]): Promise<number>;
}

interface SeedClient extends RawSqlClient {
  $connect?: () => Promise<void>;
  $disconnect?: () => Promise<void>;
  $transaction?<R>(
    fn: (tx: SeedClient) => Promise<R>,
    options?: SeedTransactionOptions
  ): Promise<R>;
}

interface SeedTransactionOptions {
  maxWait?: number;
  timeout?: number;
  isolationLevel?: string;
}

The three optional members are optional because they genuinely can be missing: the client both Prisma and ZenStack hand you inside a transaction has none of them. A client without $transaction still works with transactional: false.

SeedContext<TClient>

What a seeder receives:

interface SeedContext<TClient = SeedClient> {
  prisma: TClient;  // the injected client
  client: TClient;  // the same object, neutrally named
  logger: Logger;
  name: string;     // the seeder's filename without extension
}

Low-level helpers

Not needed for normal use:

| Export | Purpose | |---|---| | assertSeedClient(value, origin) | Throws unless value satisfies the contract. | | capabilitiesOf(client) | { transactions, connect, disconnect } — what a client can do. | | supportsTransactions(client) | Shorthand for the above. | | createLogger(options), silentLogger | The library's logger, and a no-op one. |

Exported types

SeedClient, RawSqlClient, SeedTransactionOptions, SeedClientCapabilities, SeedClientFactory, SeedClientSource, SeedContext, SeederConfig, UserSeederConfig, SeederModule, Provider, Logger, LogLevel, LoggerOptions, DiscoveredSeeder, SeedExecutionRecord, RunReport, RollbackReport, RunSeedersOptions, RollbackSeedersOptions, SeedApiOptions. PrismaLike is a deprecated alias of SeedClient.

Configuration

Optional. Read from seeder.config.ts (also .mts, .js, .mjs, .cjs), or from a prismaSeeder key in package.json.

import { defineConfig } from 'prisma-seed';

export default defineConfig({
  seedersDir: 'prisma/seeders',
  ledgerTable: 'SeedExecution',
  transactional: true,
});

| Key | Default | What it does | |---|---|---| | seedersDir | prisma/seeders | Where seeders live, relative to the project root | | schemaPath | auto-detected | File the provider is read from | | provider | from the schema | postgresql | mysql | sqlite | sqlserver. Setting it skips reading the schema entirely | | ledgerTable | SeedExecution | Validated as a SQL identifier | | transactional | true | Wrap each seeder plus its ledger write in one transaction | | transactionTimeout | 300000 | Milliseconds. Prisma's own default is 5 s, far too little for seeding. Clients that have no such option ignore it | | client | — | A client, or a (possibly async) factory returning one. The injection point | | closeClient | true | CLI only. false stops it from disconnecting a client you provided | | seederLanguage | auto-detected | ts | esm | cjs for generated files | | clientType | Prisma | { import, type } — how to type the client in generated TypeScript seeders | | freshExclude | [] | Extra tables fresh must not empty |

Precedence, lowest to highest: defaults → package.json → seeder.config.* → CLI flags.

Project and schema discovery

The project root is the nearest ancestor directory containing package.json, prisma/ or zenstack/. That is why the CLI works from any subdirectory. If no marker is found, the current directory is used.

The schema is looked for in this order, and only the provider is read from it:

  1. prisma.schema or zenstack.schema in package.json
  2. prisma/schema.prisma
  3. zenstack/schema.zmodel
  4. schema.zmodel
  5. prisma/schema.zmodel

Both provider = "postgresql" and provider = 'postgresql' are accepted.

The ledger table

Created automatically on first run:

| Column | | |---|---| | id | autoincrement primary key | | seedName | unique; the filename without extension | | batch | which run applied it | | executedAt | timestamp |

You do not have to declare it in your schema. Declaring it is still worth doing, because a schema-push command would otherwise see an extra table it wants to drop, and prisma migrate dev reports it as drift and asks to reset the database. With migrations, declare it before the first seed so that a migration creates it. Use @@map to state the table name explicitly:

model SeedExecution {
  id         Int      @id @default(autoincrement())
  seedName   String   @unique
  batch      Int      @default(1)
  executedAt DateTime @default(now())

  @@map("SeedExecution")
}

The table name is yours to choose; the column names are not. @@map must agree with ledgerTable, so in a project that maps everything to snake_case:

model SeedExecution {
  id         Int      @id @default(autoincrement())
  seedName   String   @unique
  batch      Int      @default(1)
  executedAt DateTime @default(now())

  @@map("seed_executions")
}
export default defineConfig({ ledgerTable: 'seed_executions' });

Do not put @map on the fields. The library queries "seedName", "batch" and "executedAt" as quoted identifiers, so renaming a column breaks it — unlike the table name, column names are not configurable.

Changing ledgerTable on a database that already has a ledger starts a new, empty one: every seeder becomes pending again. Rename the existing table instead if you want to keep the history.

Examples

A seed script for package.json

// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import { runSeeders } from 'prisma-seed';

const prisma = new PrismaClient();

try {
  const report = await runSeeders(prisma);
  console.log(`${report.executed.length} seeders applied (batch ${report.batch})`);
} finally {
  await prisma.$disconnect();
}

Applying a single seeder programmatically

import { PrismaClient } from '@prisma/client';
import { runSeeders } from 'prisma-seed';

const prisma = new PrismaClient();
const report = await runSeeders(prisma, { only: 'User' });
await prisma.$disconnect();

if (report.executed.length === 0) {
  console.log(report.skipped); // e.g. [{ name: '..._User', reason: 'ya ejecutado' }]
}

Inspecting a dry run

A dry run does not return the pending list — executed comes back empty and the names are written to the logger instead. Pass your own logger to capture them:

import { runSeeders, type Logger } from 'prisma-seed';

const lines: string[] = [];
const capture: Logger = {
  debug: () => {},
  info: (m) => lines.push(m),
  warn: () => {},
  error: () => {},
  success: (m) => lines.push(m),
};

await runSeeders(prisma, { dryRun: true, logger: capture });
console.log(lines); // ['Se ejecutarian 2 seeders:', '  - ..._User', '  - ..._Post']

Resetting between test runs

npx prisma-seed fresh --seed --force

Truncates every table, resets autoincrement counters so ids start at 1 again, then re-applies all seeders. Tables whose name starts with _ are never touched — that is where ORMs keep their migration bookkeeping. Add more exclusions with freshExclude.

Without --force it asks for confirmation, and when there is no interactive terminal it refuses rather than assuming yes, so it cannot wipe a database from CI by accident. --dry-run lists what it would empty.

refresh is the surgical alternative: it calls each seeder's down() and re-applies them, leaving everything else alone. It depends on your down() functions being correct; fresh depends on nothing.

Custom seeders directory

// seeder.config.ts
import { defineConfig } from 'prisma-seed';

export default defineConfig({
  seedersDir: 'db/seeds',
  ledgerTable: 'seed_history',
});

TypeScript

The package ships .d.ts declarations and a dual ESM/CJS build; both import and require resolve.

Typing the context

SeedContext is generic in the client so a seeder gets full autocomplete:

import type { PrismaClient } from '@prisma/client';
import type { SeedContext } from 'prisma-seed';

type Ctx = SeedContext<PrismaClient>;

For a non-Prisma client, use the type of your own module:

import type { db } from '../../lib/db';
type Ctx = SeedContext<typeof db>;

The generic has no constraint, deliberately: requiring one would force a cast on clients whose $transaction overloads do not line up exactly.

Running TypeScript seeders

Nothing to set up: npx prisma-seed run is the same command as in a JavaScript project. When the project has a tsconfig.json, a seeder.config.ts / .mts, or .ts seeders, the CLI registers tsx — a dependency of prisma-seed — before it loads anything. That covers:

  • seeder.config.ts, .ts seeders, and the TypeScript modules they import — your client, Prisma 7's generated client, shared helpers;
  • imports without an extension, and paths aliases (@/lib/db) from the tsconfig.json at the project root, even when the CLI runs from a subdirectory;
  • CommonJS and ESM projects alike — with or without "type" in package.json.

Node's own TypeScript support is not enough for this: it runs .ts files, but does not resolve imports without an extension nor paths aliases, and Prisma 7's generated client is full of the former.

Two things to know:

  • The programmatic API does not register anything. runSeeders() runs in your process, and how your process loads modules is yours to decide: to call it from a .ts script, run that script with tsx.
  • PRISMA_SEED_NO_TSX=1 turns the built-in loader off, for a process that already brings its own.

What generate writes

generate picks the language from, in order: --ts / --js, seederLanguage in the configuration, the extension of the seeders already present, tsconfig.json, and finally package.json#type. It prints which signal decided.

In TypeScript it imports PrismaClient from @prisma/client only when that package actually exports a generated client. Otherwise it emits a local placeholder interface plus the single line you replace to type it properly — never any. That is the case with Prisma 7's prisma-client generator: @prisma/client is installed, but the client lives in the generator's output. Declare clientType once and every seeder it writes is typed with your real client.

Advanced usage

Client lifecycle

| Who created the client | Who closes it | |---|---| | The library (new PrismaClient() fallback) | The library, always | | Your client configuration | The CLI, because it owns the process. closeClient: false prevents this | | You, via runSeeders(client) | Only you. The programmatic API never disconnects a borrowed client |

This matters: in an application the client is usually a shared module with a pool behind it. Closing it would break the next use.

Transactions

By default each seeder and its ledger write run inside one $transaction. Turn it off per run with --no-transaction, or permanently with transactional: false — needed for seeders that execute DDL, which forces an implicit commit on MySQL, and for very large loads where a long transaction holds locks.

The same options object is sent to every client; each applies what it understands and ignores the rest.

Choosing the dialect without a schema

If there is no schema file — or you do not want it read — set the engine directly:

export default defineConfig({ provider: 'postgresql' });

Troubleshooting

No se encontro el schema en "..." — no schema file was found at any of the searched locations. Set schemaPath, or skip schema reading entirely with provider.

No se encontro "@prisma/client" en ... — the project has no Prisma client and no client was configured. Either generate the Prisma client, or inject your own via seeder.config / runSeeders(client).

No se pudo cargar "@prisma/client" or No se pudo construir PrismaClient — @prisma/client is installed but cannot build a client by itself. With Prisma 7 that is expected: inject the client. With Prisma 5 or 6, run npx prisma generate.

npm error ERESOLVE ... peerOptional @prisma/client@">=5 <7" — you are installing prisma-seed 1.0.0 into a Prisma 7 project. Its peer range stopped at 7; from 1.0.1 it has no upper bound.

El cliente obtenido de ... no expone $queryRawUnsafe — the injected value does not satisfy the contract. If your ORM blocks raw SQL through access policies, inject the client without them.

... sin un cargador de TypeScript — a .ts config or seeder was loaded by a process without a TypeScript loader. The CLI brings its own, so this only happens through runSeeders() in a plain Node process (run it with tsx) or with PRISMA_SEED_NO_TSX=1. See Running TypeScript seeders.

... importa un modulo que no se encuentra — an import in the configuration, the client factory or a seeder points nowhere. Check the path; if it is an alias, it has to be declared in paths of the tsconfig.json at the project root.

"X" ya estaba ejecutado; no se vuelve a lanzar — expected. Roll it back first if you want to repeat it.

"X" no exporta una funcion "main" — a seeder must export main. If it is a CommonJS file, module.exports = { main, down } works too.

fresh vacia todas las tablas y no hay terminal para confirmarlo — you are in a non-interactive shell. Pass --force deliberately, or --dry-run to see the list first.

A seeder ran but nothing was written — check whether it threw after writing. The seeder and its ledger record share a transaction, so a failure rolls back both, and the seeder stays pending.

Compatibility

| Technology | Support | Verified | |---|---|---| | PrismaClient (@prisma/client 6) | Supported | Yes — PostgreSQL 16, MySQL 8, SQLite | | Prisma 5 | Declared in peerDependencies (>=5, optional) | Not verified | | Prisma 7 | Supported by injecting the client; the automatic path cannot choose a driver adapter | Yes — @prisma/client 7.10 on PostgreSQL 16, MySQL 8 and SQLite | | ZenStack v3 (@zenstackhq/orm 3.9.1) | Supported | Yes — PostgreSQL 17, in a Next.js 16 application | | NestJS | Supported, see NestJS | Yes — NestJS 12.1 with Prisma 7.10 and PostgreSQL 16 | | TypeScript projects | Supported with no setup: .ts config and seeders, paths aliases | Yes — CommonJS and ESM, with and without "type", from the project root and from subdirectories | | PostgreSQL | Supported | Yes | | MySQL | Supported | Yes | | SQLite | Supported | Yes | | SQL Server | Implemented | No — the dialect has unit tests but has never run against a real server | | MongoDB | Not supported | — | | Node 22, Node 24 | Supported | Yes | | Node 18, Node 20 | Not supported | Confirmed failing on 18 |

Neither Next.js nor NestJS is a dependency of prisma-seed; no framework is. They appear above only to describe the applications in which compatibility was verified.

Known limitations

  • No concurrency lock. Two simultaneous runs can apply the same seeder twice.
  • No dependency graph. SeederModule declares order and dependencies, but the runner does not act on them; ordering is by filename only.
  • fresh empties, it does not drop. Recreating the schema would mean invoking your ORM's CLI, which would couple the library to one implementation.

License

MIT


Further documentation: docs/CHANGELOG.md · docs/MIGRACION.md (coming from prisma-seeder-custom) · docs/IMPLEMENTACION.md (architecture and design decisions).