Drizzle Migrations in a Monorepo setup

2024-10-03 21:45

When working with Drizzle in a monorepo setup you will likely stumble across the issue on how to run migrations.

There are a couple of options on how to run your Drizzle migrations. In general you’ll want to use drizzle-kit migrate to do this. The command picks up your drizzle.config.ts file to get the path of the migrations folder and adds some nice console logging on the entire migrations process.

Let’s assume the following folder structure for an exemple monorepo project:

.
├── apps
│   ├── frontend
│   └── cms
└── packages
    ├── db (drizzle files)
    │   ├── drizzle.config.ts
    │   └── migrations
    │       └── ...
    └── ui

In a monorepo this will limit executing drizzle-kit migrate to only be run from the package that contains the drizzle files (packages/db), so either a seperate database package, or the app itself if you chose not put drizzle in a seperate package.

To run a migration from other apps that import and use the drizzle package, you can use the migrate function. The code for this looks like something like:

import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { db, client } from '@org/db';

async function runMigrations() {
  console.log('Running migrations');
  await migrate(db, { migrationsFolder: `./migrations` });
  console.log('Migrations complete');
  await client.end();
}

runMigrations();

db and client are standard instances to interact with drizzle. Read more in the docs. You might have to adjust this script by loading environment vars to get the DB credentials

The key part here is the path for migrationsFolder, as it has to be accessible for this script to run. So in our folder structure from above, this script would work if it’s executed inside the db package, but not from within the cms or frontend apps. This can be solved by adjusting the folder path to a relative path, something like.

...
  await migrate(db, { migrationsFolder: `../../packages/db/migrations` });
...

Now the script can be run inside the app or frontend folder, when they are run within a context of the entire monorepo codebase.

Running a migration in a container without access to the entire codebase

In a containerized setup you will most likely want to run a migration when your app container starts and depending on your final build, only the app folder (cms or frontend) will be available as context to run the script.

The solution here is to adjust your container build (i.e. your Dockerfile) and copy over the migrations folder into the final build of the app, so that the migrations is accessible.

So this could be something like:

...
COPY --from=build /app/packages/db/drizzle/migrations migrations
...

Now the migrations folder would be available in the app container. Next steps would be to:

  • Adjust the migrationsFolder to match the path from the Dockerfile for your migrations script
  • Add a new command to your package.json to run this migration script.
  • Adjust your container entrypoint command or start script to run this migration

Further reading