Getting Started
Database Drivers
Schema Integrations
Seed Generators
Utilities and Testing
The following files were used as context for generating this wiki page:
Schema declarations in Drizzle define the structure of your database, acting as the single source of truth for the ORM. They bridge the gap between physical database tables and the TypeScript types required for type-safe query building. By utilizing declarative constructs like pgTable, mysqlTable, and sqliteTable, developers specify column names, data types, constraints, and relationships that allow the library to introspect and communicate with the underlying database engine effectively.
This subsystem provides both a runtime representation (used by drivers to construct SQL) and a compile-time type inference engine. The declarations handle mapping database-specific types (e.g., serial, uuid, jsonb) to their TypeScript equivalents, managing default values, and defining complex constraints like primary keys, foreign keys, and indexes.
In the context of drizzle-kit, these declarations are further utilized by introspection tools to reverse-engineer existing database schemas into code. The system converts raw schema metadata into high-fidelity TypeScript code, ensuring that the generated output maintains naming conventions, relationship mappings, and structural constraints that the Drizzle core understands.
At the core of schema declarations are the table definition functions, which accept a table name and an object mapping column names to their definitions. Each column definition function (e.g., text(), integer()) acts as a builder, allowing for method chaining to apply modifiers such as .notNull(), .primaryKey(), or .default().
The construction process is designed for composability. By separating the column name, the database name, and the constraint modifiers, Drizzle ensures that runtime execution paths can accurately map internal definitions to SQL syntax while type-checkers verify that query results align with the schema.
// Example of a table declaration using pgTable
import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users_table', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
age: integer('age'),
});Sources: drizzle-orm/type-tests/pg/tables.ts:87-105
The drizzle-kit introspectors (e.g., introspect-pg.ts, introspect-mysql.ts) operate by traversing database-specific internal schemas and serializing them into executable TypeScript code. The mechanism begins by collecting tables, enums, sequences, and views, then iteratively generating import statements and table declarations.
The core flow for schemaToTypeScript involves:
pgTable, index, foreignKey).createTableColumns.createTableIndexes, createTablePKs, etc.Sources: drizzle-kit/src/introspect-pg.ts:309-636
Mapping database types to Drizzle columns involves resolving type-specific metadata and defaults. The mapDefault function handles the resolution of default values by checking the type and the internal configuration of the column.
Note
When internals.tables[tableName].columns[name].isDefaultAnExpression is true, the system treats the default as an SQL expression, wrapping it in sql\ `` to ensure it is not treated as a string literal.
The mechanism includes guard logic to prevent incorrect type-casting:
isArray check: If a column is an array, buildArrayDefault is called.uuid (using .defaultRandom()), timestamp (using .defaultNow()), and others.Sources: drizzle-kit/src/introspect-pg.ts:673-836
Foreign keys are registered by iterating through a table's foreignKeys collection. The introspector tracks existing relationships to detect cyclic dependencies, which helps in correctly ordering or annotating references.
The mechanism for creating foreign key references ensures that if a table references itself, the generator correctly uses table as the reference target instead of the generated variable name.
Warning
The current introspector implementation has specific limitations regarding naming; custom references in .references() are sometimes switched off in the generator to avoid potential naming collisions or complexity with generated identifiers.
Sources: drizzle-kit/src/introspect-pg.ts:1343-1369
Indexes, primary keys, and unique constraints are defined as a factory function parameter after the column mapping. This is critical for supporting composite constraints that span multiple columns. The createTableIndexes function takes a list of indexes and processes their metadata to produce valid index statements.
The selection logic for index naming is determined by indexName: if an index name matches the default format (e.g., tablename_column1_column2_index), it remains implicit, otherwise, it is explicitly escaped as a string.
Sources: drizzle-kit/src/introspect-pg.ts:1201-1257
Sources: drizzle-kit/src/introspect-pg.ts:168-177
The process of generating a table's TypeScript representation follows a specific hierarchy:
schemaToTypeScript invokes Object.values(schema.tables).map(...).createTableColumns is called for the column definitions.column() is triggered for every column iteration, returning the column statement string.createTableIndexes or createTableFKs are called to append the constraint array factory function.statement += ');' finalizes the table definition.This sequential ordering ensures that the output file structure is consistent, grouping definitions, constraints, and final initialization code.
Sources: drizzle-kit/src/introspect-pg.ts:508-565