Relevant source files
The following files were used as context for generating this wiki page:
- drizzle-seed/src/services/SeedService.ts
- drizzle-seed/src/index.ts
- drizzle-kit/src/api.ts
- drizzle-seed/src/services/Generators.ts
- drizzle-kit/src/introspect-mysql.ts
- drizzle-kit/src/serializer/gelSerializer.ts
- drizzle-kit/src/serializer/studio.ts
- drizzle-kit/src/introspect-singlestore.ts
- drizzle-seed/src/types/seedService.ts
- drizzle-seed/type-tests/mysql.ts
- drizzle-seed/type-tests/sqlite.ts
- drizzle-kit/src/sqlgenerator.ts
- drizzle-seed/type-tests/pg.ts
- drizzle-seed/src/types/drizzleStudio.ts
- drizzle-seed/src/services/apiVersion.ts
- drizzle-seed/src/services/utils.ts
Overview
The seed service engine is a deterministic test data generation framework built into Drizzle ORM that populates database tables with realistic mock data, handles schema introspection, and executes database mutations [drizzle-seed/src/index.ts:301-364, Seed Data Generators]. It provides the core engine behind the Seed Client Interface, bridging TypeScript table definitions and relation metadata with robust random value generators while supporting versioned generator configurations and user-defined refinements drizzle-seed/src/index.ts:196-205, drizzle-seed/src/index.ts:301-364, drizzle-seed/src/services/SeedService.ts:305-337. The engine addresses complex database population challenges such as foreign key dependencies, cyclic table relations, and type-safe generator mapping across multiple SQL dialects [drizzle-seed/src/services/SeedService.ts:339-483, drizzle-seed/src/services/SeedService.ts:503-991, Database Introspection, Migration Toolkit Schema]. By employing topological sorting for insertion ordering and deferred updates for cyclic references, the engine enables reliable batch seeding and resetting for PostgreSQL, MySQL, and SQLite databases [drizzle-seed/src/services/SeedService.ts:375-422, drizzle-seed/src/services/SeedService.ts:911-961, Studio Server Bridge].
Sources: drizzle-seed/src/index.ts:196-205, drizzle-seed/src/index.ts:301-364, drizzle-seed/src/services/SeedService.ts:305-337, drizzle-seed/src/services/SeedService.ts:339-483, drizzle-seed/src/services/SeedService.ts:503-991
Seed Client Interface
The public client interface for the seed service engine centers around the top-level seed() and reset() entrypoint functions, which accept a database connection, a schema definition object, and an optional configuration object drizzle-seed/src/index.ts:346-364, drizzle-seed/type-tests/mysql.ts:4. The seed() function returns a SeedPromise instance, enabling promise chaining and invocation of the .refine() method drizzle-seed/src/index.ts:310-363. The .refine() method accepts a callback function providing versioned generator functions (funcs), allowing users to customize column-level mock data generation and table row counts drizzle-seed/src/index.ts:196-205, drizzle-seed/src/index.ts:324-342.
export function seed<
DB extends
| PgDatabase<any, any>
| MySqlDatabase<any, any, any, any>
| BaseSQLiteDatabase<any, any>,
SCHEMA extends {
[key: string]:
| PgTable
| PgSchema
| MySqlTable
| MySqlSchema
| SQLiteTable
| Relations
| any;
},
VERSION extends '2' | '1' | undefined,
>(db: DB, schema: SCHEMA, options?: { count?: number; seed?: number; version?: VERSION }) {
return new SeedPromise<typeof db, typeof schema, VERSION>(db, schema, options);
}Sources: drizzle-seed/src/index.ts:346-364
The options object supports a global count parameter specifying the default number of rows to generate per table, a deterministic seed number to control the pseudo-random number generator state, and a version string ('1' or '2') selecting between generator function API versions drizzle-seed/src/index.ts:196-210, drizzle-seed/src/index.ts:304-309, drizzle-seed/src/index.ts:362. When options.version is undefined or set to '2', the engine resolves generator functions from generatorsFuncsV2; otherwise, it uses legacy generatorsFuncs drizzle-seed/src/index.ts:200-202, drizzle-seed/src/index.ts:208-210.
Note
Passing an explicit seed number guarantees deterministic data generation: running the seeding script multiple times with the same seed value yields identical database content, provided the schema and refinement definitions remain unchanged drizzle-seed/src/index.ts:308.
Sources: drizzle-seed/src/index.ts:308
| Option Key | Type | Default | Description |
|---|---|---|---|
count | number | undefined | Number of rows to generate per table globally. |
seed | number | undefined | Seed value controlling the pseudo-random generator state. |
version | '1' | '2' | undefined | Version specifier ('2' maps to generatorsFuncsV2, '1' maps to generatorsFuncs). |
Sources: drizzle-seed/src/index.ts:196-210, drizzle-seed/src/index.ts:304-309, drizzle-seed/src/index.ts:362
Sources: drizzle-seed/src/index.ts:196-205, drizzle-seed/src/index.ts:346-364
Dialect Schema Inspection
The dialect schema inspection phase processes database schemas and metadata representations from Drizzle Studio or active database connections, transforming raw table definitions into standardized internal models drizzle-seed/src/index.ts:216-260, drizzle-seed/src/index.ts:366-380. When seeding via Drizzle Studio objects, the engine iterates over schemas, tables, and columns, extracting properties such as column names, data types, nullability, defaults, uniqueness constraints, and primary keys drizzle-seed/src/index.ts:238-260. Composite primary keys and column-level primary flags populate the table's primary key list drizzle-seed/src/index.ts:258.
export type DbPrimaryKey = {
name: string;
columns: string[];
};
export type DbForeignKey = {
name: string;
tableFrom: string;
columnsFrom: string[];
schemaFrom: string;
schemaTo: string;
tableTo: string;
columnsTo: string[];
onUpdate?: string;
onDelete?: string;
};Sources: drizzle-seed/src/types/drizzleStudio.ts:1-16
For relational wiring, seedForDrizzleStudio extracts and filters Drizzle Studio relations where the relation type is 'one' and matches the target schema boundaries drizzle-seed/src/index.ts:237, drizzle-seed/src/index.ts:263. It evaluates reciprocal relation pairs to detect cyclic dependencies, tagging each relation with an isCyclic boolean flag before generating possible column generators drizzle-seed/src/index.ts:264-285. MySQL table filtering filters schema entries to isolate MySqlTable and Relations instances, mapping database table names back to their TypeScript counterparts via getTableName drizzle-seed/src/index.ts:886-928, drizzle-seed/src/index.ts:963-973.
| Property | Type | Description |
|---|---|---|
name | string | Constraint identifier name. |
columnsFrom / columnsTo | string[] | Source and target columns involved in the foreign key relationship. |
schemaFrom / schemaTo | string | Source and target database schema names. |
tableFrom / tableTo | string | Source and target table names. |
onUpdate / onDelete | string (optional) | Referential action rules for updates and deletes. |
Sources: drizzle-seed/src/types/drizzleStudio.ts:6-16
Note
Relations are filtered specifically for 'one' types during Drizzle Studio seeding initialization to correctly build structural parent-child table trees before generating row data drizzle-seed/src/index.ts:237.
Sources: drizzle-seed/src/index.ts:237
Dependency Graph Ordering
The engine determines insertion sequence by inspecting table relationships and analyzing inbound and outbound dependency edges drizzle-seed/src/services/SeedService.ts:424-483. The method getInfoFromRelations aggregates relation arrays into a lookup map containing inbound and outbound counts, self-reference flags, and sets of required or dependent table names drizzle-seed/src/services/SeedService.ts:424-480. Leaf tables—defined as those with zero outbound relations or where all outbound edges are self-references—seed the initial queue for topological sorting drizzle-seed/src/services/SeedService.ts:378-385.
getOrderedTablesList = (
tablesInOutRelations: ReturnType<typeof this.getInfoFromRelations>['tablesInOutRelations'],
): string[] => {
const leafTablesNames = Object.entries(tablesInOutRelations)
.filter(
(tableRel) =>
tableRel[1].out === 0
|| (tableRel[1].out !== 0
&& tableRel[1].selfRelCount === tableRel[1].out),
)
.map((tableRel) => tableRel[0]);
const orderedTablesNames: string[] = [];
let parent: string, children: string[];
for (let i = 0; leafTablesNames.length !== 0; i++) {
parent = leafTablesNames.shift() as string;
if (orderedTablesNames.includes(parent)) {
continue;
}
if (tablesInOutRelations[parent] === undefined) {
orderedTablesNames.push(parent);
continue;
}
for (const orderedTableName of orderedTablesNames) {
tablesInOutRelations[parent]!.requiredTableNames.delete(orderedTableName);
}
if (
tablesInOutRelations[parent]!.requiredTableNames.size === 0
|| equalSets(
tablesInOutRelations[parent]!.requiredTableNames,
tablesInOutRelations[parent]!.dependantTableNames,
)
) {
orderedTablesNames.push(parent);
} else {
leafTablesNames.push(...tablesInOutRelations[parent]!.requiredTableNames, parent);
continue;
}
children = [...tablesInOutRelations[parent]!.dependantTableNames];
leafTablesNames.push(...children);
}
return orderedTablesNames;
};Sources: drizzle-seed/src/services/SeedService.ts:375-422
Cyclic relations are evaluated via depth-first search in isRelationCyclic to identify back-edges, and cyclicTablesCompare resolves ordering priority between tables involved in circular dependencies drizzle-seed/src/services/SeedService.ts:339-373, drizzle-seed/src/services/SeedService.ts:832-864. If both tables in a cyclic pair contain notNull foreign key columns, the engine rejects the schema configuration, as circular non-null constraints prevent sequential insertion drizzle-seed/src/services/SeedService.ts:355-360.
| Relation Property | Type | Description |
|---|---|---|
out | number | Count of outbound foreign key references originating from the table. |
in | number | Count of inbound foreign key references pointing to the table. |
selfRelation | boolean | Indicates whether the relation references the table itself. |
selfRelCount | number | Number of columns involved in a self-reference. |
requiredTableNames | Set<string> | Set of prerequisite tables that must be seeded first. |
dependantTableNames | Set<string> | Set of dependent tables requiring this table's primary keys. |
Sources: drizzle-seed/src/services/SeedService.ts:425-433
Caution
Attempting to seed cyclic tables where both tables contain notNull foreign key columns triggers an immediate error, because neither table can be populated first without violating constraints drizzle-seed/src/services/SeedService.ts:355-360.
Sources: drizzle-seed/src/services/SeedService.ts:355-360
Column Generator Selection
Version Selection and Generator Mapping
The seed engine maps column data types, base types, and uniqueness constraints to concrete generator instances by invoking dialect-specific pickers like selectGeneratorForSqlite and selectGeneratorForPostgresColumn. Once an initial generator is instantiated, replaceIfUnique handles columns with uniqueness constraints by substituting specialized unique variant generators drizzle-seed/src/services/SeedService.ts:283-286. The resulting generator instance is then passed to selectVersionOfGenerator, which inspects the entity kind and resolves constructors defined in generatorsMap according to the active API version drizzle-seed/src/services/SeedService.ts:289, drizzle-seed/src/services/SeedService.ts:305-316.
selectVersionOfGenerator = (generator: AbstractGenerator<any>) => {
const entityKind = generator.getEntityKind();
if (entityKind === 'GenerateArray') {
const oldBaseColumnGen = (generator as GenerateArray).params.baseColumnGen;
const newBaseColumnGen = this.selectVersionOfGenerator(oldBaseColumnGen);
// newGenerator.baseColumnDataType = oldGenerator.baseColumnDataType;
(generator as GenerateArray).params.baseColumnGen = newBaseColumnGen;
}
const possibleGeneratorConstructors = generatorsMap[entityKind as keyof typeof generatorsMap];
const possibleGeneratorConstructorsFiltered = possibleGeneratorConstructors?.filter((possGenCon) =>
possGenCon.version <= this.version! // sorting in ascending order by version
).sort((a, b) => a.version - b.version);
const generatorConstructor = possibleGeneratorConstructorsFiltered?.at(-1) as
| (new(params: any) => AbstractGenerator<any>)
| undefined;
if (generatorConstructor === undefined) {
throw new Error(`Can't select ${entityKind} generator for ${this.version} version.`);
}
const newGenerator = new generatorConstructor(generator.params);
newGenerator.baseColumnDataType = generator.baseColumnDataType;
newGenerator.isUnique = generator.isUnique;
// TODO: for now only GenerateValuesFromArray support notNull property
newGenerator.notNull = generator.notNull;
newGenerator.dataType = generator.dataType;
newGenerator.stringLength = generator.stringLength;
return newGenerator;
};Sources: drizzle-seed/src/services/SeedService.ts:305-337
The latest supported engine version is tracked as a constant, and filtering logic sorts available generator constructors in ascending order by version, selecting the highest version that does not exceed this.version drizzle-seed/src/services/SeedService.ts:316-321, drizzle-seed/src/services/apiVersion.ts:1-1. For nested structures such as GenerateArray, the version selection procedure recursively traverses and resolves base column generators before instantiating the wrapper drizzle-seed/src/services/SeedService.ts:307-314.
| Property / Field | Type | Description |
|---|---|---|
columnName | string | Target column name in the database schema. |
generator | AbstractGenerator<any> | undefined | Active mock data generator assigned to the column. |
isUnique | boolean | Flag indicating whether uniqueness constraints apply. |
notNull | boolean | Flag indicating whether null values are forbidden. |
primary | boolean | Flag indicating whether the column forms part of the primary key. |
generatedIdentityType | 'always' | 'byDefault' | undefined | Identity generation strategy for auto-incrementing columns. |
wasRefined | boolean | Indicates if the generator was modified by user refinements. |
wasDefinedBefore | boolean | Tracking flag for prior definition state. |
isCyclic | boolean | Indicates involvement in a cyclic table relationship. |
Sources: drizzle-seed/src/types/seedService.ts:14-24
Note
When selecting generator versions, if possibleGeneratorConstructorsFiltered finds no matching constructor for the requested version, an explicit error is thrown indicating that the generator cannot be selected drizzle-seed/src/services/SeedService.ts:324-326.
Sources: drizzle-seed/src/services/SeedService.ts:324-326
SQLite Column Type Selection Rules
For SQLite schemas, selectGeneratorForSqlite inspects primary keys, column types, and data types to assign the appropriate generator instance drizzle-seed/src/services/SeedService.ts:993-997. Integer primary keys map directly to GenerateIntPrimaryKey, whereas boolean flags and timestamps map to GenerateBoolean and GenerateTimestamp respectively drizzle-seed/src/services/SeedService.ts:999-1015. Numeric columns with precision and scale parameters calculate a maxAbsoluteValue to bound GenerateNumber parameters drizzle-seed/src/services/SeedService.ts:1026-1038.
selectGeneratorForSqlite = (
table: Table,
col: Column,
) => {
const pickGenerator = (table: Table, col: Column) => {
// int section ---------------------------------------------------------------------------------------
if (
(col.columnType === 'integer' || col.columnType === 'numeric')
&& table.primaryKeys.includes(col.name)
) {
const generator = new generatorsMap.GenerateIntPrimaryKey[0]();
return generator;
}
if (col.columnType === 'integer' && col.dataType === 'boolean') {
const generator = new generatorsMap.GenerateBoolean[0]();
return generator;
}
if ((col.columnType === 'integer' && col.dataType === 'date')) {
const generator = new generatorsMap.GenerateTimestamp[0]();
return generator;
}
if (
col.columnType === 'integer'
|| (col.dataType === 'bigint' && col.columnType === 'blob')
) {
const generator = new generatorsMap.GenerateInt[0]();
return generator;
}
// number section ------------------------------------------------------------------------------------
if (col.columnType.startsWith('real') || col.columnType.startsWith('numeric')) {
if (col.typeParams.precision !== undefined) {
const precision = col.typeParams.precision;
const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale;
const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale);
const generator = new generatorsMap.GenerateNumber[0]({
minValue: -maxAbsoluteValue,
maxValue: maxAbsoluteValue,
precision: Math.pow(10, scale),
});
return generator;
}
const generator = new generatorsMap.GenerateNumber[0]();
return generator;
}
Sources: drizzle-seed/src/services/SeedService.ts:993-1043
Text, numeric, and blob columns undergo pattern matching against column names to assign domain-specific generators such as GenerateFirstName and GenerateEmail, falling back to GenerateString or GenerateUniqueString depending on primary key status drizzle-seed/src/services/SeedService.ts:1045-1083.
| Column Condition | Column Type / Metadata | Assigned Generator |
|---|---|---|
| Primary Key Integer | integer / numeric + primary key | GenerateIntPrimaryKey |
| Boolean Flag | integer + dataType: 'boolean' | GenerateBoolean |
| Timestamp | integer + dataType: 'date' | GenerateTimestamp |
| Standard Integer | integer / blob (bigint) | GenerateInt |
| Bounded Number | real / numeric + precision params | GenerateNumber (with custom bounds) |
| Unique Text | text / numeric / blob + primary key | GenerateUniqueString |
| Name Field | text / numeric / blob + name includes 'name' | GenerateFirstName |
| Email Field | text / numeric / blob + name includes 'email' | GenerateEmail |
Sources: drizzle-seed/src/services/SeedService.ts:999-1073
Warning
Property propagation during version selection currently copies notNull, dataType, and stringLength properties, but the engine notes that only GenerateValuesFromArray fully supports runtime enforcement of the notNull property drizzle-seed/src/services/SeedService.ts:291-293, drizzle-seed/src/services/SeedService.ts:331-333.
Sources: drizzle-seed/src/services/SeedService.ts:291-293, drizzle-seed/src/services/SeedService.ts:331-333
Database Mutation Execution
Cyclic Table Validation and Ordering
Before executing record generation and database insertion, seedMySql inspects schema relationships to identify cyclic dependencies drizzle-seed/src/index.ts:926-928. When tables participate in cyclic relationships, preserveCyclicTablesData is enabled to manage foreign key constraints across interdependent entities drizzle-seed/src/index.ts:939-946.
Caution
cyclicTablesCompare explicitly throws an error if two cyclic tables both contain notNull foreign key columns, as the engine cannot seed cyclic relationships with mandatory foreign keys drizzle-seed/src/services/SeedService.ts:355-359.
Sources: drizzle-seed/src/services/SeedService.ts:355-359
cyclicTablesCompare = (
table1: Table,
table2: Table,
relation: Relation & { isCyclic: boolean },
reverseRelation: Relation & { isCyclic: boolean } | undefined,
) => {
// TODO: revise
const hasTable1NotNullColumns = relation.columns.some((colIName) =>
table1.columns.find((colJ) => colJ.name === colIName)?.notNull === true
);
if (reverseRelation !== undefined) {
const hasTable2NotNullColumns = reverseRelation.columns.some((colIName) =>
table2.columns.find((colJ) => colJ.name === colIName)?.notNull === true
);
if (hasTable1NotNullColumns && hasTable2NotNullColumns) {
throw new Error(
`The '${table1.name}' and '${table2.name}' tables have not null foreign keys. You can't seed cyclic tables with not null foreign key columns.`,
);
}
if (hasTable1NotNullColumns) return 1;
else if (hasTable2NotNullColumns) return -1;
return 0;
}Sources: drizzle-seed/src/services/SeedService.ts:339-364
Table Value Generation and Insertion Pipeline
SeedService drives the generation of record values across ordered topological tables and handles subsequent filtering of cyclic relations to perform secondary updates drizzle-seed/src/index.ts:941-960. For MySQL environments, resetMySql temporarily disables foreign key checks by executing SET FOREIGN_KEY_CHECKS = 0;, truncates each target table using raw SQL statements, and restores foreign key checks with SET FOREIGN_KEY_CHECKS = 1; before inserting generated batches drizzle-seed/src/index.ts:867-884.
| Step | Action | Execution Details |
|---|---|---|
| 1 | Table Filtering & Info Extraction | Filters schema definitions into valid MySQL tables and relations via filterMysqlTables and getMySqlInfo drizzle-seed/src/index.ts:926-927. |
| 2 | Initial Value Generation | Generates initial table values via generateTablesValues with cyclic preservation enabled drizzle-seed/src/index.ts:941-947. |
| 3 | Cyclic Filtering | Filters cyclic table generators using filterCyclicTables to isolate unique non-null columns needing deferred updates drizzle-seed/src/index.ts:949-952. |
| 4 | Secondary Database Update | Executes subsequent row updates in the database when filtered generators are present drizzle-seed/src/index.ts:954-960. |
Sources: drizzle-seed/src/index.ts:926-960
Related
On This Page
OverviewSeed Client InterfaceDialect Schema InspectionDependency Graph OrderingColumn Generator SelectionVersion Selection and Generator MappingSQLite Column Type Selection RulesDatabase Mutation ExecutionCyclic Table Validation and OrderingTable Value Generation and Insertion PipelineRelated