prisma-seed
v1.1.0
Published
Versioned, reversible database seeding for Prisma projects: run-once seeders, batches and rollback, with Laravel-style commands.
Maintainers
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-seednpx 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 batchThe 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
(
SeedExecutionby default). Running twice does nothing the second time. - Batches. Each
rungroups its seeders into a numbered batch, sorollbackcan revert exactly the last one. - Reversible. Each seeder exports
down();rollback,refreshandrollback --alluse 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 callsnew 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/rollbackSeedersfor aseed.tsof 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,tsconfigpathsaliases included. - Scaffolding.
generatewrites 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
batchcolumn 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-seedNothing 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-seedVariants:
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 seedersReference
| 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 -- --forcePrismaClient
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 runThe 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 itrunSeeders 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/client7.10, injected throughseeder.config.ts, on PostgreSQL 16 (@prisma/adapter-pg), MySQL 8 (@prisma/adapter-mariadb) and SQLite (@prisma/adapter-better-sqlite3):run,status,rollback,refreshandfresh --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 runFrom 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 generateWith 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 ofprisma-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 oneIf 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/cli12.0), Prisma 7.10 with theprisma-clientgenerator writing tosrc/generated/prisma,@prisma/adapter-pgand PostgreSQL 16, in a CommonJS project whosetsconfig.build.jsonsetsrootDir: "./src". NestJS is not a dependency ofprisma-seed.
1. Install
npm install --save-dev prisma-seedNothing 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 againseed: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_URLis already set:docker compose exec api npm run seed.dotenvdoes 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 nextmigrate devreportsDrift detected ... [+] Added tables - SeedExecutionand 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-seedis a dev dependency, so an image built withnpm ci --omit=devdoes not have it. Andrun,rollback,refreshandfreshrefuse to run withNODE_ENV=productionunless 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 withprisma-seed <command>.
Usage
1. Create a seeder
npx prisma-seed generate UserThis 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 pendientesCLI 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 # everythingExecution order
Seeders run in filename order, computed as:
- Files with a
YYYYMMDDHHmmss_prefix first, oldest first. - Files without a prefix after them, alphabetically.
- 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 userAn already-applied seeder is not re-run. To repeat one:
npx prisma-seed rollback User && npx prisma-seed run UserAPI
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:
prisma.schemaorzenstack.schemainpackage.jsonprisma/schema.prismazenstack/schema.zmodelschema.zmodelprisma/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 --forceTruncates 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,.tsseeders, and the TypeScript modules they import — your client, Prisma 7's generated client, shared helpers;- imports without an extension, and
pathsaliases (@/lib/db) from thetsconfig.jsonat the project root, even when the CLI runs from a subdirectory; - CommonJS and ESM projects alike — with or without
"type"inpackage.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.tsscript, run that script withtsx. PRISMA_SEED_NO_TSX=1turns 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.
SeederModuledeclaresorderanddependencies, but the runner does not act on them; ordering is by filename only. freshempties, it does not drop. Recreating the schema would mean invoking your ORM's CLI, which would couple the library to one implementation.
License
Further documentation: docs/CHANGELOG.md ·
docs/MIGRACION.md (coming from prisma-seeder-custom) ·
docs/IMPLEMENTACION.md (architecture and design
decisions).
