3.5.7. Migrations
In this chapter, you'll learn what a migration is and how to generate a migration or write it manually.
What is a Migration?#
A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
The migration's file has a class with two methods:
- The
up
method reflects changes on the database. - The
down
method reverts the changes made in theup
method.
Generate Migration#
Instead of you writing the migration manually, the Medusa CLI tool provides a db:generate command to generate a migration for a modules' data models.
For example, assuming you have a blog
Module, you can generate a migration for it by running the following command:
This generates a migration file under the migrations
directory of the Blog Module. You can then run it to reflect the changes in the database as mentioned in this section.
Write a Migration Manually#
You can also write migrations manually. To do that, create a file in the migrations
directory of the module and in it, a class that has an up
and down
method. The class's name should be of the format Migration{YEAR}{MONTH}{DAY}{HOUR}{MINUTE}.ts
to ensure migrations are ran in the correct order.
For example:
1import { Migration } from "@mikro-orm/migrations"2 3export class Migration202507021059 extends Migration {4 5 async up(): Promise<void> {6 this.addSql("create table if not exists \"author\" (\"id\" text not null, \"name\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"author_pkey\" primary key (\"id\"));")7 }8 9 async down(): Promise<void> {10 this.addSql("drop table if exists \"author\" cascade;")11 }12 13}
The migration class in the file extends the Migration
class imported from @mikro-orm/migrations
. In the up
and down
method of the migration class, you use the addSql
method provided by MikroORM's Migration
class to run PostgreSQL syntax.
In the example above, the up
method creates the table author
, and the down
method drops the table if the migration is reverted.
Run the Migration#
To run your migration, run the following command:
--skip-links
option.This reflects the changes in the database as implemented in the migration's up
method.
Rollback the Migration#
To rollback or revert the last migration you ran for a module, run the following command:
This rolls back the last ran migration on the Blog Module.
Caution: Rollback Migration before Deleting#
If you need to delete a migration file, make sure to rollback the migration first. Otherwise, you might encounter issues when generating and running new migrations.
For example, if you delete the migration of the Blog Module, then try to create a new one, Medusa will create a brand new migration that re-creates the tables or indices. If those are still in the database, you might encounter errors.
So, always rollback the migration before deleting it.
More Database Commands#
To learn more about the Medusa CLI's database commands, refer to this CLI reference.
Data Migration Scripts#
In some use cases, you may need to perform data migration after updates to the database. For example, after you added a Site
data model to the Blog Module, you want to assign all existing posts and authors to a default site. Another example is updating data stored in a third-party system.
In those scenarios, you can instead create a data migration script. They are asynchronous function that the Medusa application executes once when you run the npx medusa db:migrate command
.
How to Create a Data Migration Script#
You can create data migration scripts in a TypeScript or JavaScript file under the src/migration-scripts
directory. The file must export an asynchronous function that will be executed when the db:migrate
command is executed.
For example, to create a data migration script for the Blog Module example, create the file src/migration-scripts/migrate-blog-data.ts
with the following content:
1import { MedusaModule } from "@medusajs/framework/modules-sdk";2import { ExecArgs } from "@medusajs/framework/types";3import { BLOG_MODULE } from "../modules/blog";4import { createWorkflow } from "@medusajs/framework/workflows-sdk";5 6export default async function migrateBlogData({ container }: ExecArgs) {7 // Check that the blog module exists8 if (!MedusaModule.isInstalled(BLOG_MODULE)) {9 return10 }11 12 await migrateBlogDataWorkflow(container).run({})13}14 15const migrateBlogDataWorkflow = createWorkflow(16 "migrate-blog-data",17 () => {18 // Assuming you have these steps19 createDefaultSiteStep()20 21 assignBlogDataToSiteStep()22 }23)
In the above example, you default export an asynchronous function that receives an object parameter with the Medusa Container property.
In the function, you first ensure that the Blog Module is installed to avoid errors otherwise. Then, you run a workflow that you've created in the same file that performs the necessary data migration.
Test Data Migration Script
To test out the data migration script, run the migration command:
Medusa will run any pending migrations and migration scripts, including your script.
If the script runs successfully, Medusa won't run the script again.
If there are errors in the script, you'll receive an error in the migration script logs. Medusa will keep running the script every time you run the migration command until it runs successfully.