Skip to main content

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

To download and install the latest release of the Atlas CLI, simply run the following in your terminal:

curl -sSf https://atlasgo.sh | sh

We will use the following packages for this guide:

  • drizzle-orm - Drizzle ORM package
  • drizzle-kit - Drizzle CLI
  • pg - PostgreSQL client for Node.js
  • tsx - TypeScript compiler
npm i drizzle-orm pg dotenv
npm i -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:

  1. New users table model

Create a new file schema.ts in your project root folder:

schema.ts
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(),
});

  1. Setup Drizzle config file

Create a drizzle.config.ts file in your project root folder:

drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
out: './drizzle',
schema: './schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
  1. Run the drizzle generate command to generate the first migration:
npx drizzle-kit generate
  1. Create a PostgreSQL development database with Docker:
docker run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16
  1. 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:

  1. At the project root folder, create atlas.hcl file:
atlas.hcl
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.

  1. 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.

  1. 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.

  1. 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.

  1. Adding a new model to the Drizzle schema

Let's add a new Post model to the Drizzle schema:

schema.ts
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'),
});
  1. 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.

atlas/migrations/20250102090452.sql
-- Create "posts" table
CREATE TABLE "posts" ("id" serial NOT NULL, "content" text NULL, "author_id" integer NULL, PRIMARY KEY ("id"));
  1. Apply the new migration plan to the development database:
atlas migrate apply \
--env local \
--url "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
Shell output
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
  1. 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:

Shell output
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:

atlas/drizzle_objects.sql
-- Create "echo" function
CREATE FUNCTION "echo" (text) RETURNS text LANGUAGE sql AS $$ SELECT $1; $$;

After that, edit the atlas.hcl file to include the atlas/drizzle_objects.sql with composite schemas

atlas.hcl
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.

atlas/migrations/20250102095509.sql
-- 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
note

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