Content Migrations in Contember
Introduction
Content migrations within Contember are designed to manage and transform the existing data in your database. This is achieved using GraphQL queries, which provide a powerful and versatile approach to data manipulation.
Setting Up Content Migration Files
Content migration files should be added to the api/migrations directory in your Contember project. The naming convention for these files is YYYY-MM-DD-HHMMSS-label, which ensures they are executed in the correct order and are easy to identify.
Example File Name: 2023-10-26-150000-add-english-locale.js
Using the CLI to create blank migration files
You can use the Contember CLI to create blank migration files. Simply run the following command and provide a label for your migration.
yarn contember migration:blank <label>
Depending on the complexity of your migration, you can choose to use a JSON, JavaScript (JS), or TypeScript (TS) file. By default, the CLI will create a TypeScript file.
Defining Migrations in JSON
Content migrations can be defined in JSON, using a single "query" or an array of "queries".
Single Query Example
{
  "query": "mutation { createLocale(data: { code: \"en\" }) { ok errorMessage } }"
}
Multiple Queries Example
{
  "queries": [
    {
      "query": "mutation { createLocale(data: { code: \"en\" }) { ok errorMessage } }"
    },
    {
      "query": "mutation { createLocale(data: { code: \"fr\" }) { ok errorMessage } }"
    }
  ]
}
Defining Migrations in JavaScript or TypeScript
For more advanced use cases, JS or TS can be used to define migrations.
Single Query with Variables
export default {
  query: `
    mutation CreateLocale($data: LocaleCreateInput) {
      createLocale(data: $data) {
        ok
        errorMessage
      }
    }
  `,
  variables: {
    data: {
      code: "en"
    }
  }
};
Multiple Queries with Variables
export default {
  queries: [
    {
      query: `
        mutation CreateLocale($data: LocaleCreateInput) {
          createLocale(data: $data) {
            ok
            errorMessage
          }
        }
      `,
      variables: {
        data: {
          code: "en"
        }
      }
    },
    { 
      // Second query
    }
  ]
};
Instead of default export, you can use named exports "queries" and "query" to define migrations.
Utilizing Functions in Migrations
Functions in migration files provide a higher level of control and the ability to perform dynamic operations.
export default async function () {
  // Fetch data, execute queries, and perform other asynchronous operations here.
  // Return content migrations if needed
  return {
    queries: [
        // Individual queries
    ]
  };
};
Validating Mutation Results
It's crucial to fetch the "ok" and "errorMessage" fields for top-level mutations to ensure they have executed successfully. Contember CLI checks these fields by default, but you can bypass this check (not recommended) by including checkMutationResult: false in your query object.
Applying Migrations
To run your content migrations, use the Contember CLI’s migration:execute command.
npx contember migration:execute
Execution Model for Function Migrations
Function migrations introduce a unique execution model. The CLI orchestrates the migrations, grouping non-function migrations into a single transaction, while function migrations are executed separately, each in its own transaction.
Execution Flow Example:
- 
Transaction 1: - Schema Migration A
- Content Migration B
- Schema Migration C
 
- 
Function Migration (Separate Transaction): - Execute exported function and await result
- If the function returns content migrations, send them to the migration API
 
- 
Subsequent Non-Function Migrations (New Transaction) 
Note: Due to this model, it’s advisable to run function migrations independently to avoid complications, as some migrations might succeed even if others fail.