Setting up Drizzle with Atlas
TL;DR
- Drizzle is a headless TypeScript ORM
- Atlas is a database schema management tool, based on modern DevOps principles.
- Developers using Drizzle to build their applications can use Atlas to manage their database schema,
covering advanced use cases not supported by Drizzle's native
migrate
command.
Automatic migration planning for Drizzle
Drizzle is a modern, open-source ORM for TypeScript and Node.js that simplifies database interactions with a SQL-like syntax. It combines the power of TypeScript with the simplicity of SQL, making it easy for developers to work with databases.
Drizzle also offers powerful schema management tools, including the drizzle-kit generate
and drizzle-kit migrate
commands for planning and applying database schema changes.
While Drizzle’s migration system is optimized for ORM-specific tasks and portability, more advanced schema management or complex database configurations may benefit from specialized tools like Atlas
By integrating with Atlas, Drizzle users can leverage features such as:
Feature | Description |
---|---|
Database Features | Automatic migration planning for advanced database objects such as Views, Stored Procedures, Triggers, Row Level Security, etc. |
Continuous Integration | Catch issues before they hit production with robust GitHub Actions, GitLab, and CircleCI Orbs integrations. Detect risky migrations, test data migrations, database functions, and more. |
Continuous Delivery | Atlas can be integrated into your pipelines to provide native integrations with your deployment machinery (e.g. Kubernetes Operator, Terraform, etc.) |
Schema Monitoring | Atlas can monitor your database schema and alert you when it drifts away from its expected state. |
How to Atlas + Drizzle
Atlas integrates seamlessly with Drizzle using the external_schema
data source. This allows
you to use your existing Drizzle schema as the source schema for Atlas, enabling you utilize all Atlas features for
managing your database.
Atlas replaces the drizzle-kit migrate
command, providing you with a dedicated tool for schema changes.
If you need to extend your database schema beyond what Drizzle supports, Atlas can help you manage these objects using
the composite_schema
data source which enables you to compose
database schemas from multiple sources.
When to use Atlas instead of drizzle-kit migrate
As mentioned above, Drizzle's migrate
command is a great tool for many use cases. However, there are some scenarios where
you may want to consider using Atlas instead:
- You are building a Platform. If you are building an Internal Developer Platform (IDP) for your company and need to support "Paved Paths" for multiple ORMs and programming languages, Atlas can provide a consistent way to manage database schemas across all your projects.
- You need robust CI/CD. If you need to ensure that your database schema changes are tested and linted before they are applied to production or want to natively integrate migrations into your CD machinery (e.g. Kubernetes, Terraform, GitHub Actions, ArgoCD, FluxCD, etc.), it is useful to choose a tool that has all of these features built-in.
- You need to manage advanced database objects. If you need to manage advanced database objects such as Views, Stored Procedures, Triggers, Row Level Security, etc., Atlas provides a way to manage these objects in a declarative way.
Setting up Drizzle with Atlas
This guide explains how to configure Atlas to automatically plan migrations for your Drizzle project. It is ideal for those starting a new Drizzle project and using Atlas as the initial migration planning tool.
Installation Atlas & Drizzle CLI
- macOS + Linux
- Homebrew
- Docker
- Windows
- Manual Installation
To download and install the latest release of the Atlas CLI, simply run the following in your terminal:
curl -sSf https://atlasgo.sh | sh
Get the latest release with Homebrew:
brew install ariga/tap/atlas
To pull the Atlas image and run it as a Docker container:
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host
flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
We will use the following packages for this guide:
drizzle-orm
- Drizzle ORM packagedrizzle-kit
- Drizzle CLIpg
- PostgreSQL client for Node.jstsx
- TypeScript compiler
- npm
- yarn
- pnpm
- bun
npm i drizzle-orm pg
npm i -D drizzle-kit tsx @types/pg
yarn add drizzle-orm pg
yarn add -D drizzle-kit tsx @types/pg
pnpm add drizzle-orm pg
pnpm add -D drizzle-kit tsx @types/pg
bun add drizzle-orm pg
bun add -D drizzle-kit tsx @types/pg
New Drizzle Project
Follow the steps below to create simple Drizzle project with a User
model:
- New
users
table model
Create a new file schema.ts
in your project root folder:
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
export const usersTable = pgTable("users", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
age: integer().notNull(),
email: varchar({ length: 255 }).notNull().unique(),
});
- Setup Drizzle config file
Create a drizzle.config.ts
file in your project root folder:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './schema.ts',
dialect: 'postgresql'
});
- Run Drizzle export command
Run the following command to check if we configured Drizzle correctly:
npx drizzle-kit export
If everything is set up correctly, you should see the following output in the terminal:
CREATE TABLE "users" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "users_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"name" varchar(255) NOT NULL,
"age" integer NOT NULL,
"email" varchar(255) NOT NULL,
CONSTRAINT "users_email_unique" UNIQUE("email")
);
After those steps, your project folder should look like this:
my-drizzle-project
├── schema.ts
└── drizzle.config.ts
Configuring Atlas to use Drizzle schema
To configure Atlas to use your Drizzle schema, you need to export the schema to SQL format. Atlas supports the external_schema data source, which allows you to run an external program to generate the schema.
Let's create atlas.hcl
in your project root folder:
data "external_schema" "drizzle" {
program = [
"npx",
"drizzle-kit",
"export",
]
}
env "local" {
dev = "docker://postgres/16/dev?search_path=public"
schema {
src = data.external_schema.drizzle.url
}
migration {
dir = "file://atlas/migrations"
}
}
In the above configuration, we utilize the drizzle-kit export
command to generate a full DDL.
The generated Data Definition Language (DDL) will be used as the source schema for Atlas.
Applying the schema to a target database
Next, let's show how to apply the schema to a target database using Atlas.
First, create a PostgreSQL development database with Docker:
docker run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:latest
Then, run the following command to apply the schema to the development database:
atlas schema apply \
--env local \
--url "postgresql://postgres:postgres@:5432/postgres?search_path=public&sslmode=disable"
Atlas will load the desired state of the database schema from the schema.ts
file and compare it with the current
state of the database. Next, Atlas will generate a migration plan and prompt you for approval:
Planning migration statements (1 in total):
-- create "users" table:
-> CREATE TABLE "users" (
"id" integer NOT NULL GENERATED ALWAYS AS IDENTITY,
"name" character varying(255) NOT NULL,
"age" integer NOT NULL,
"email" character varying(255) NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "users_email_unique" UNIQUE ("email")
);
-------------------------------------------
? Approve or abort the plan:
▸ Approve and apply
Abort
After approving the plan, Atlas will apply the migration to the target database:
Applying approved migration (1 statement in total):
-- create "users" table
-> CREATE TABLE "users" (
"id" integer NOT NULL GENERATED ALWAYS AS IDENTITY,
"name" character varying(255) NOT NULL,
"age" integer NOT NULL,
"email" character varying(255) NOT NULL,
PRIMARY KEY ("id"),
CONSTRAINT "users_email_unique" UNIQUE ("email")
);
-- ok (19.349625ms)
-------------------------
-- 19.427333ms
-- 1 migration
-- 1 sql statement
Let's try to re-run the command to apply the schema to the target database:
atlas schema apply \
--env local \
--url "postgresql://postgres:postgres@:5432/postgres?search_path=public&sslmode=disable"
As expected, Atlas detects that the schema is already up-to-date and does not apply any changes:
Schema is synced, no changes to be made
This flow (schema apply
) is called the "Declarative Workflow" in Atlas, and you can learn more about it
here.
Running Atlas to plan migrations
Alternatively, Atlas can be used to drive a more traditional "Versioned Workflow" for managing database schema changes.
In the context of Drizzle, this means you can run Atlas to plan migrations instead of using the drizzle generate
command.
Based on our existing setup from the previous steps, let's use the atlas migrate diff
command to generate a migration plan:
atlas migrate diff --env local
This command will generate migration plans in the atlas/migrations
folder:
my-drizzle-project
│── atlas
│ └── migrations
│ └── 20241227051915.sql
| └── atlas.sum
├── atlas.hcl
├── schema.ts
└── drizzle.config.ts
20241017062735.sql
contains the migration plan for the new User
model:
-- Create "users" table
CREATE TABLE "users" ("id" integer NOT NULL GENERATED ALWAYS AS IDENTITY, "name" character varying(255) NOT NULL, "age" integer NOT NULL, "email" character varying(255) NOT NULL, PRIMARY KEY ("id"), CONSTRAINT "users_email_unique" UNIQUE ("email"));
Amazing! Atlas automatically generated a migration file that will create the User
table in our database.
To apply the migrations against our local database, let's first clean up the existing database:
atlas schema clean --env local --url "postgresql://postgres:postgres@:5432/postgres?search_path=public&sslmode=disable"
Atlas suggests dropping some resources:
Planning migration statements (1 in total):
-- drop "user" table:
-> DROP TABLE "User";
-------------------------------------------
? Approve or abort the plan:
▸ Approve and apply
Abort
After approving and applying the plan, we are left with an empty database.
Now, we can finally apply the migrations to our local database:
atlas migrate apply --env local --url "postgresql://postgres:postgres@:5432/postgres?search_path=public&sslmode=disable"
Atlas prints the following output:
Migrating to version 20250103022514 (1 migrations in total):
-- migrating version 20250103022514
-> CREATE TABLE "users" ("id" integer NOT NULL GENERATED ALWAYS AS IDENTITY, "name" character varying(255) NOT NULL, "age" integer NOT NULL, "email" character varying(255) NOT NULL, PRIMARY KEY ("id"), CONSTRAINT "users_email_unique" UNIQUE ("email"));
-- ok (2.750834ms)
-------------------------
-- 41.373917ms
-- 1 migration
-- 1 sql statement
This flow (migrate apply
) is called the "Versioned Workflow" in Atlas, and you can learn more about it
here.