Integrating Atlas with an existing Drizzle project
This guide explains how to configure Atlas to automatically plan migrations for your existing Drizzle project and manage untracked objects using Atlas.
Installation Atlas & Drizzle
- 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 dotenv
npm i -D drizzle-kit tsx @types/pg
yarn add drizzle-orm pg dotenv
yarn add -D drizzle-kit tsx @types/pg
pnpm add drizzle-orm pg dotenv
pnpm add -D drizzle-kit tsx @types/pg
bun add drizzle-orm pg dotenv
bun add -D drizzle-kit tsx @types/pg
If you have an existing Drizzle project with a drizzle
folder already applied to the target database,
you can move your project to using Atlas with a few adjustments.
In this section, we demonstrate how to replace drizzle-kit migrate
with Atlas for managing your database schema.
Create an example Drizzle project
To mimic the scenario of an existing project, let's create a new Drizzle project with PostgreSQL database,
with an existing User
model in the target database.
Follow these steps to create a new Drizzle project:
- 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 users = 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',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
- Run the
drizzle generate
command to generate the first migration:
npx drizzle-kit generate
- Create a PostgreSQL development database with Docker:
docker run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16
- Run the
drizzle-kit migrate
command to apply the migration to the development database:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres npx drizzle-kit migrate
After those steps, your project folder should look like this:
my-drizzle-project
├── drizzle
| ├── meta
| | ├── ...
| ├── 0000_heavy_swordsman.sql
├── schema.ts
└── drizzle.config.ts
Configuring Atlas to use Drizzle schema
Now that we have an existing Drizzle project from the previous section,
let's see how we can use Atlas to replace the drizzle-kit migrate
command.
Follow these steps to configure Atlas to use the Drizzle schema:
- At the project root folder, create
atlas.hcl
file:
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"
}
exclude = ["drizzle"]
}
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.
This config excludes the drizzle
schema from the migration plan, as it is already applied to the database.
- Generate the migration plan using
atlas migrate diff
:
atlas migrate diff --env local
You should see the migration plan generated by Atlas at the atlas/migrations
folder.
- Apply generated migrations to development database with
baseline
atlas migrate apply \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable" \
--baseline 20250102081546
The --baseline
flag tells Atlas to apply the migration plan starting from the specified timestamp.
- Let's run
atlas migrate status
to verify the migration status:
atlas migrate status \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
You should see the migration status as up
for the applied migrations.
Migration Status: OK
-- Current Version: 20250102081546
-- Next Version: Already at latest version
-- Executed Files: 1
-- Pending Files: 0
Running Atlas to plan migrations
After transitioning your existing Drizzle project to use Atlas for managing migrations, let's see how your workflow will look like when using Atlas.
- Adding a new model to the Drizzle schema
Let's add a new Post
model to the Drizzle schema:
import { integer, pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
age: integer().notNull(),
email: varchar({ length: 255 }).notNull().unique(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
});
- Generate the migration plan using
atlas migrate diff
:
atlas migrate diff --env local
You should see the new migration plan generated by Atlas at the atlas/migrations
folder.
-- Create "posts" table
CREATE TABLE "posts" ("id" serial NOT NULL, "content" text NULL, "author_id" integer NULL, PRIMARY KEY ("id"));
- Apply the new migration plan to the development database:
atlas migrate apply \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
Migrating to version 20250102090452 from 20250102081546 (1 migrations in total):
-- migrating version 20250102090452
-> CREATE TABLE "posts" ("id" serial NOT NULL, "content" text NULL, "author_id" integer NULL, PRIMARY KEY ("id"));
-- ok (11.096792ms)
-------------------------
-- 22.413167ms
-- 1 migration
-- 1 sql statement
- Verify the migration status:
atlas migrate status \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
You should see the current version as 20250102090452
:
Migration Status: OK
-- Current Version: 20250102090452
-- Next Version: Already at latest version
-- Executed Files: 2
-- Pending Files: 0
Managing untracked objects
In some cases, you might have untracked objects in the database that manually created inside the Drizzle migration files or created directly in the database. To manage these untracked objects, you can use Atlas to inspect them and convert them into the Atlas schema.
To demonstrate this, let's manually add a function
object to the development database:
Run by docker command to connect to the development database:
docker exec -it postgres psql -U postgres -d postgres -c 'CREATE FUNCTION public.echo (text) RETURNS text LANGUAGE sql AS $$ SELECT $1; $$;'
At this point, we have a function
in the database that is not managed by Drizzle.
The idea is to use atlas schema diff
command to compare the target database with our external schema.
Ok, let's create an atlas/drizzle_objects.sql
file to store these untracked objects:
atlas schema diff \
--env local \
--from "file://atlas/migrations" \
--to "postgresql://postgres:postgres@localhost:5432/postgres?search_path=public&sslmode=disable" \
--exclude "atlas_schema_revisions" > atlas/drizzle_objects.sql
The atlas/drizzle_objects.sql file should contain the function
object:
-- Create "echo" function
CREATE FUNCTION "echo" (text) RETURNS text LANGUAGE sql AS $$ SELECT $1; $$;
After that, edit the atlas.hc
l file to include the atlas/drizzle_objects.sql
with composite schemas
data "external_schema" "drizzle" {
program = [
"npx",
"drizzle-kit",
"export",
]
}
data "composite_schema" "drizzle-objects" {
schema "public" {
url = data.external_schema.drizzle.url
}
schema "public" {
url = "file://atlas/drizzle_objects.sql"
}
}
env "local" {
dev = "docker://postgres/16/dev?search_path=public"
schema {
src = data.composite_schema.drizzle-objects.url
}
migration {
dir = "file://atlas/migrations"
}
exclude = ["drizzle"]
}
Run this command to plan migrations with Atlas:
atlas migrate diff --env local
You should see the echo
function included in the migration plan.
-- Create "echo" function
CREATE FUNCTION "echo" (text) RETURNS text LANGUAGE sql AS $$ SELECT $1; $$;
In the last step, set the baseline version to the latest migration version, to avoid applying duplicates to the database:
atlas migrate apply \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable" \
--baseline 20250102095509
Since the baseline version can be set only once, if you have already set the database to a different
baseline version (as described in the section above), you can use the atlas migrate set
command instead:
atlas migrate set 20250102095509 \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
This command sets the database to version 20241018071458
. Hence, only migration files with versions
higher than this will be applied to the database. This is intended, as these objects are already in the database.
The code for this tutorial is available under providers/drizzle