Create migration scripts with data-ops
Changing your content model often means updating existing content to match the new structure. Migration scripts
help you automate content changes across your project so you can update items in bulk without manually changing them through the Management API. This lesson walks you through creating migration scripts with data-ops.
When to use migration scripts
Migration scripts help you automate content transformations across your Kontent.ai project. Reach for one whenever you need to:- Update content items after making changes to your content model
- Bulk-update element values across many items at once
- Restructure content from one content type to an updated one
- Apply consistent changes across language variants
- Transform content as part of an environment sync or deployment workflow
The building blocks of content migration
When migrating content within Kontent.ai, you’re working with:- Source – the existing content in your Kontent.ai environment that needs to be updated
- Migration logic – the rules and operations that define how content should be modified
- Destination – the Kontent.ai environment where the transformed content will be saved
Before you start
Make sure you have the following ready:- Node.js (LTS version)
- The data-ops
package – run it directly with
npx(as shown in the example below) or install it locally
Example content migration scenario
Let’s say you have a project with blog articles. Each article has an author typed manually as “Jane Doe” into a text element. At some point, you decide to include additional information about the authors, such as their bios and profile images. Because adding these details to each article would take a long time and be hard to keep in sync, you create a new content type called Author. Then you link the Author content items to the articles. The example script below covers only the first part of that change, creating the Author type with a name and a bio. Any other element, such as one for the profile image, is added the same way.Create a migration script
To automate a content change, start by creating a migration script. Themigrations add command generates a Kontent.ai migration script file. The generated script contains a module object with three properties:
order– a unique number or date that determines execution sequence, with number-based ordering always running before date-based. By default,migrations addcreates each migration file withorder: 1, so assign a unique order value before running the migration.run– the function that performs the migration, using the Management SDK client passed in as a parameter (namedapiClientin the generated script).rollback(optional) – the function that reverses the migration, if you need to undo it later.
--timestamp to migrations add to name the file with the current UTC date and time, and set order to that same value, ensuring each new script has a unique order value assigned automatically.my-first-migration, run:
npx @kontent-ai/data-ops@latest migrations add \
--migrationsFolder my-first-migration \
--name my-first-migration \
--type jsconst migration = {
order: 1,
run: async apiClient => {},
rollback: async apiClient => {},
};
export default migration;--type js in the command, migrations add generates TypeScript instead (the CLI’s default). Keep in mind migrations run only ever executes JavaScript, so a .ts migration needs to be transpiled into ES Modules with your own TypeScript setup before you can run it.run function. For the scenario above, that means creating the Author type and its elements:
run: async apiClient => {
await apiClient
.addContentType()
.withData(() => ({
name: "Author",
elements: [
{ name: "Name", type: "text" },
{ name: "Bio", type: "rich_text" },
],
}))
.toPromise();
},Write a rollback function
Therollback function reverses whatever run did, so you have a way back if a migration turns out wrong. For the Author type from the example above, that means deleting it:
rollback: async apiClient => {
await apiClient
.deleteContentType()
.byTypeCodename("author")
.toPromise()
.catch(e => console.log(`Unsuccessful deletion of type Author: ${e}`));
},.catch() at the end. It’s a good idea to wrap deletions like this so the rollback still completes even if the original migration only got partway through. Without it, trying to delete something that was never created would cause the whole rollback to fail right when you need it most.
rollback function is valid, which means the migration runs and the rollback later completes without changing anything or reporting an error. You only discover it's empty when you need it, so write it together with run.Keep your scripts maintainable
A few practices keep your scripts manageable as your migrations folder grows:- One script per task. It’s easier to tell where something failed when each script does one thing.
- Hardcoded vs. dynamic values. For a one-off script, like updating one specific content item, hardcoding the values you’re changing works fine. For a script you’ll adapt or re-run against different content, it’s recommended to retrieve values dynamically by looking up the content types, items, or elements you need through the Management API at runtime, rather than hardcoding specific IDs or codenames into the script.
- Commit migrations to version control. Treat them like database migrations, because the folder is your change history for the environment.
Run your scripts safely
You now have a migration script that creates the Author type, with a rollback that removes it.Your scripts don’t change anything until you run them. Running them is covered in a separate lesson on purpose. A migration pointed at the wrong environment changes live content immediately, and the only way to undo it is the rollback function you wrote.Continue with Safe content migrations in production to execute them, starting in a non-production environment, validating against a fresh copy of production, and only then touching production itself.