Skip to main content
doc0blade47/drizzle-ormdocumentation home
Sign in

Seed Service Engine

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

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 KeyTypeDefaultDescription
countnumberundefinedNumber of rows to generate per table globally.
seednumberundefinedSeed value controlling the pseudo-random generator state.
version'1' | '2'undefinedVersion 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.

PropertyTypeDescription
namestringConstraint identifier name.
columnsFrom / columnsTostring[]Source and target columns involved in the foreign key relationship.
schemaFrom / schemaTostringSource and target database schema names.
tableFrom / tableTostringSource and target table names.
onUpdate / onDeletestring (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 PropertyTypeDescription
outnumberCount of outbound foreign key references originating from the table.
innumberCount of inbound foreign key references pointing to the table.
selfRelationbooleanIndicates whether the relation references the table itself.
selfRelCountnumberNumber of columns involved in a self-reference.
requiredTableNamesSet<string>Set of prerequisite tables that must be seeded first.
dependantTableNamesSet<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 / FieldTypeDescription
columnNamestringTarget column name in the database schema.
generatorAbstractGenerator<any> | undefinedActive mock data generator assigned to the column.
isUniquebooleanFlag indicating whether uniqueness constraints apply.
notNullbooleanFlag indicating whether null values are forbidden.
primarybooleanFlag indicating whether the column forms part of the primary key.
generatedIdentityType'always' | 'byDefault' | undefinedIdentity generation strategy for auto-incrementing columns.
wasRefinedbooleanIndicates if the generator was modified by user refinements.
wasDefinedBeforebooleanTracking flag for prior definition state.
isCyclicbooleanIndicates 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 ConditionColumn Type / MetadataAssigned Generator
Primary Key Integerinteger / numeric + primary keyGenerateIntPrimaryKey
Boolean Flaginteger + dataType: 'boolean'GenerateBoolean
Timestampinteger + dataType: 'date'GenerateTimestamp
Standard Integerinteger / blob (bigint)GenerateInt
Bounded Numberreal / numeric + precision paramsGenerateNumber (with custom bounds)
Unique Texttext / numeric / blob + primary keyGenerateUniqueString
Name Fieldtext / numeric / blob + name includes 'name'GenerateFirstName
Email Fieldtext / 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.

StepActionExecution Details
1Table Filtering & Info ExtractionFilters schema definitions into valid MySQL tables and relations via filterMysqlTables and getMySqlInfo drizzle-seed/src/index.ts:926-927.
2Initial Value GenerationGenerates initial table values via generateTablesValues with cyclic preservation enabled drizzle-seed/src/index.ts:941-947.
3Cyclic FilteringFilters cyclic table generators using filterCyclicTables to isolate unique non-null columns needing deferred updates drizzle-seed/src/index.ts:949-952.
4Secondary Database UpdateExecutes 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

Was this page helpful?