Become a Gold Sponsor

Drizzle migrations fundamentals

SQL databases require you to specify a strict schema of entities you’re going to store upfront and if (when) you need to change the shape of those entities - you will need to do it via schema migrations.

There’re multiple production grade ways of managing database migrations. Drizzle is designed to perfectly suite all of them, regardless of you going database first or codebase first.

Database first is when your database schema is a source of truth. You manage your database schema either directly on the database or via database migration tools and then you pull your database schema to your codebase application level entities.

Codebase first is when database schema in your codebase is a source of truth and is under version control. You declare and manage your database schema in JavaScript/TypeScript and then you apply that schema to the database itself either with Drizzle, directly or via external migration tools.

How can Drizzle help?

We’ve built drizzle-kit - CLI app for managing migrations with Drizzle.

drizzle-kit migrate
drizzle-kit generate
drizzle-kit push
drizzle-kit pull

It is designed to let you choose how to approach migrations based on your current business demands.

It fits in both database and codebase first approaches, it lets you push your schema or generate SQL migration files or pull the schema from database. It is perfect wether you work alone or in a team.



Now let’s pick the best option for your project:

Option 1

I manage database schema myself using external migration tools or by running SQL migrations directly on my database. From Drizzle I just need to get current state of the schema from my database and save it as TypeScript schema file.

Expand details

That’s a database first approach. You have your database schema as a source of truth and Drizzle let’s you pull database schema to TypeScript using drizzle-kit pull command.

                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 
                                  β”‚                        β”‚ <---  CREATE TABLE "users" (
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚                        β”‚        "id" SERIAL PRIMARY KEY,
β”‚ ~ drizzle-kit pull       β”‚      β”‚                        β”‚        "name" TEXT,
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚        DATABASE        β”‚        "email" TEXT UNIQUE
  β”‚                               β”‚                        β”‚       );
  β”” Pull datatabase schema -----> β”‚                        β”‚
  β”Œ Generate Drizzle       <----- β”‚                        β”‚
  β”‚ schema TypeScript file        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚
  v
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), 
};
Option 2

I want to have database schema in my TypeScript codebase, I don’t wanna deal with SQL migration files.
I want Drizzle to β€œpush” my schema directly to the database

Expand details

That’s a codebase first approach. You have your TypeScript Drizzle schema as a source of truth and Drizzle let’s you push schema changes to the database using drizzle-kit push command.

That’s the best approach for rapid prototyping and we’ve seen dozens of teams and solo developers successfully using it as a primary migrations flow in their production applications.

src/schema.ts
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), // <--- added column
};
Add column to `users` table                                                                          
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ + email: text().unique() β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           
  v                                           
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ ~ drizzle-kit push       β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”” Pull current datatabase schema ---------> β”‚                          β”‚
                                              β”‚                          β”‚
  β”Œ Generate alternations based on diff <---- β”‚         DATABASE         β”‚
  β”‚                                           β”‚                          β”‚
  β”” Apply migrations to the database -------> β”‚                          β”‚
                                       β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   ALTER TABLE `users` ADD COLUMN `email` TEXT UNIQUE; 
Option 3

I want to have database schema in my TypeScript codebase, I want Drizzle to generate SQL migration files for me and apply them to my database

Expand details

That’s a codebase first approach. You have your TypeScript Drizzle schema as a source of truth and Drizzle let’s you generate SQL migration files based on your schema changes with drizzle-kit generate and then apply them to the database with drizzle-kit migrate commands.

src/schema.ts
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), 
};
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ $ drizzle-kit generate β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           
  β”” 1. read previous migration folders
    2. find diff between current and previous schema
    3. prompt developer for renames if necessary
  β”Œ 4. generate SQL migration and persist to file
  β”‚    β”Œβ”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  
  β”‚      πŸ“‚ drizzle       
  β”‚      β”” πŸ“‚ 20242409125510_premium_mister_fear
  β”‚        β”œ πŸ“œ snapshot.json
  β”‚        β”” πŸ“œ migration.sql
  v
-- drizzle/20242409125510_premium_mister_fear/migration.sql

CREATE TABLE "users" (
 "id" SERIAL PRIMARY KEY,
 "name" TEXT,
 "email" TEXT UNIQUE
);
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ $ drizzle-kit migrate β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                         
  β”” 1. read migration.sql files in migrations folder        β”‚                          β”‚
    2. fetch migration history from database -------------> β”‚                          β”‚
  β”Œ 3. pick previously unapplied migrations <-------------- β”‚         DATABASE         β”‚
  β”” 4. apply new migration to the database ---------------> β”‚                          β”‚
                                                            β”‚                          β”‚
                                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
[βœ“] done!                                                 
Option 4

I want to have database schema in my TypeScript codebase, I want Drizzle to generate SQL migration files for me and I want Drizzle to apply them during runtime

Expand details

That’s a codebase first approach. You have your TypeScript Drizzle schema as a source of truth and Drizzle let’s you generate SQL migration files based on your schema changes with drizzle-kit generate and then you can apply them to the database during runtime of your application.

This approach is widely used for monolithic applications when you apply database migrations during zero downtime deployment and rollback DDL changes if something fails. This is also used in serverless deployments with migrations running in custom resource once during deployment process.

src/schema.ts
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), 
};
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ $ drizzle-kit generate β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           
  β”” 1. read previous migration folders
    2. find diff between current and previous schema
    3. prompt developer for renames if necessary
  β”Œ 4. generate SQL migration and persist to file
  β”‚    β”Œβ”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  
  β”‚      πŸ“‚ drizzle       
  β”‚      β”” πŸ“‚ 20242409125510_premium_mister_fear
  β”‚        β”œ πŸ“œ snapshot.json
  β”‚        β”” πŸ“œ migration.sql
  v
-- drizzle/20242409125510_premium_mister_fear/migration.sql

CREATE TABLE "users" (
 "id" SERIAL PRIMARY KEY,
 "name" TEXT,
 "email" TEXT UNIQUE
);
// index.ts
import { drizzle } from "drizzle-orm/node-postgres"
import { migrate } from 'drizzle-orm/node-postgres/migrator';

const db = drizzle(process.env.DATABASE_URL);

await migrate(db);
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ npx tsx src/index.ts  β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                                      
  β”œ 1. init database connection                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                         
  β”” 2. read migration.sql files in migrations folder        β”‚                          β”‚
    3. fetch migration history from database -------------> β”‚                          β”‚
  β”Œ 4. pick previously unapplied migrations <-------------- β”‚         DATABASE         β”‚
  β”” 5. apply new migration to the database ---------------> β”‚                          β”‚
                                                            β”‚                          β”‚
                                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
[βœ“] done!                                                 
Option 5

I want to have database schema in my TypeScript codebase, I want Drizzle to generate SQL migration files for me, but I will apply them to my database myself or via external migration tools

Expand details

That’s a codebase first approach. You have your TypeScript Drizzle schema as a source of truth and Drizzle let’s you generate SQL migration files based on your schema changes with drizzle-kit generate and then you can apply them to the database either directly or via external migration tools.

src/schema.ts
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), 
};
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ $ drizzle-kit generate β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           
  β”” 1. read previous migration folders
    2. find diff between current and previous scheama
    3. prompt developer for renames if necessary
  β”Œ 4. generate SQL migration and persist to file
  β”‚    β”Œβ”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  
  β”‚      πŸ“‚ drizzle       
  β”‚      β”” πŸ“‚ 20242409125510_premium_mister_fear
  β”‚        β”œ πŸ“œ snapshot.json
  β”‚        β”” πŸ“œ migration.sql
  v
-- drizzle/20242409125510_premium_mister_fear/migration.sql

CREATE TABLE "users" (
 "id" SERIAL PRIMARY KEY,
 "name" TEXT,
 "email" TEXT UNIQUE
);
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ (._.) now you run your migrations β”‚           
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  
  β”‚
 directly to the database
  β”‚                                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€>β”‚                    β”‚  
  β”‚                                    β”‚    β”‚      Database      β”‚           
 or via external tools                 β”‚    β”‚                    β”‚   
  β”‚                                    β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚      
  └──│ Bytebase           β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  
     β”‚ Liquibase          β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ 
     β”‚ Atlas              β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ 
     β”‚ etc…               β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

[βœ“] done!                                                 
Option 6

I want to have database schema in my TypeScript codebase, I want Drizzle to output the SQL representation of my Drizzle schema to the console, and I will apply them to my database via Atlas

Expand details

That’s a codebase first approach. You have your TypeScript Drizzle schema as a source of truth and Drizzle let’s you export SQL statements based on your schema changes with drizzle-kit export and then you can apply them to the database via external migration tools.

src/schema.ts
import * as p from "drizzle-orm/pg-core";

export const users = p.pgTable("users", {
  id: p.serial().primaryKey(),
  name: p.text(),
  email: p.text().unique(), 
};
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ $ drizzle-kit export   β”‚                  
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  
  β”‚                                           
  β”” 1. read your drizzle schema
    2. generated SQL representation of your schema
  β”Œ 4. outputs to console
  β”‚    
  β”‚       
  β”‚      
  β”‚        
  β”‚        
  v
CREATE TABLE "users" (
 "id" SERIAL PRIMARY KEY,
 "name" TEXT,
 "email" TEXT UNIQUE
);
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  
β”‚ (._.) now you run your migrations β”‚           
β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  
  β”‚
 via Atlas
  β”‚                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚              β”‚
  └──│ Atlas              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€>β”‚  Database    β”‚      
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚              β”‚       
                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

[βœ“] done!