How AI-ready is your team? Take our AI Readiness Assessment to find out how you score — now live.

Supercharging Storyblok with the CLI: Syncing, CI/CD, Type Safety, and Content Migrations

Developers
Daniel Mendoza

Storyblok is the first headless CMS that works for developers & marketers alike.

Sync, migrate, typegen: these are all trigger words for CMS developers. We know this means creating and managing complex scripts and logic to perform these actions. To make your life as a Storyblok developer easier, we provide the CLI to run complex operations by leveraging the Storyblok Management API.

If you are a Storyblok developer, it is more than likely that you have stumbled upon our developer documentation, specifically our CLI documentation. In this tutorial, we go beyond the documentation and present how you can leverage the CLI in the real-world within your development workflow.

Learn:

Did you know that this is the second iteration of the Storyblok CLI? After receiving feedback and feature requests from existing Storyblok customers, we revamped the CLI to meet the needs of real Storyblok developers.

If you are more of a visual learner and want to have a good laugh, check out our CLI Video Series!

First steps: installation and authentication

First, you need to install the CLI. Open a terminal and run the following command:

npm install -g storyblok@latest

This ensures that you are installing the latest and greatest version of our CLI.

Next, you need to authenticate with the CLI. Login by running:

storyblok login

Then follow the prompts to log in and select your desired region.

If you do not have a Storyblok account yet, we have provided a very developer-centric way to signup:

storyblok signup

This directs you to the signup page within your default browser. Fill out the required information, confirm your account, and you are good to go!

Commands and format

First, let’s go over some basics that will help you understand the CLI a bit more.

The storyblok <domain> <verb> format keeps related actions together and makes discovery more intuitive.

For example, imagine you need to pull components from a space (1234), make local changes, and then push them to a second space (5678).

storyblok components pull --space 1234

This command saves your space components in a dedicated local .storyblok folder. You can later push these changes to your target space.

storyblok components push --space 5678 --from 1234

The same applies to all commands. For example, to generate type definitions for space 1234, run the command:

storyblok types generate --space 1234

See all available commands in our CLI documentation.

Keep in mind when using the CLI, you must always specify the space that your are targeting via the Space ID. When you are forming a command, remember to add the --space option. See all of the available options here. Alternatively, you can create a configuration file.

Lastly, if you’re stuck, run or append the -h or --help option to a command. To receive high-level help, run:

storyblok -h

For more granular information on a specific command, run:

storyblok <domain> <verb> -h

This outputs the available options for that specific command.

Create a new Storyblok project

Okay, now that you are logged in, it is time to create a new project. You can create a fully working project, meaning you can run the project locally, have it hooked up to your Storyblok space, and verify that the Storyblok integration (including the Visual Editor) is working as expected. using the command:

storyblok create my-app 

After following the prompts asking to select your technology blueprint and organization (if applicable), you are directed to the Storyblok app to finalize the creation of your space. This is where you select your desired plan (you can select the Starter plan for free).

There you have it! You now have a fully working local project!

Hint:

For more information on the options, see the create documentation.

Creating multiple development environments

Typically, you need to set up multiple development environments. We need an environment for active development and a stable environment for production.

To do this:

  1. Navigate to your Spaces view within the Storyblok web app.
  2. Locate the space you just created, “my-app”.
  3. Click on options (the ellipsis on the upper righthand corner of the card).
  4. Select “Duplicate”.
  5. Name the new space. This will serve as the production space - so let’s name it My App Production
  6. Assign the space to the same organization that the my-app space lives in (if applicable - you may not belong to an organization)
  7. Duplicate the space

You now have a development space my-app and a productions space my-app-production. As of now, both spaces contain the same content and schema. But what happens when you start making changes in the dev environment? Read on to find out more!

Working in your dev environment

Now it’s time to start developing new features. Create a simple content type block to support blog articles within your development my-app.

This can be done within the blocks tab via the Storyblok web app. Here is the following schema for the article content type block:

**Technical Name:** article

**Block type:** Content type block

**Fields:**
- **Body**: Rich text
- **Summary**: Text
Hint:

For more information on content modeling concepts, visit the content-modeling, blocks, and fields documentation.

Sync component schemas across spaces

Now that you have created a new feature within your development environment, your production schema is no longer in sync with your development schema. If you want to release this new feature to production, you have to sync these schemas and make the new feature available within your production space. You also want to include your schema versions in source control so you can easily track schema changes in relation to releases and make rollbacks easier.

First, pull your current schema:

storyblok components pull --space <space-id>
Hint:

Visit the components pull documentation for more information on this command.

The command downloads all components as separate files within the desired path. You should see the article component within the list of files.

Now that you have pulled your schema and versioned it within your codebase, you can push the desired schema changes to our production space.

storyblok components push --space <space-id> --from <source-space-id>

Here --space refers to the target space ID (your production space ID) and --from refers to the source space, so this will be the development space ID, or the same space ID you used for the previous command.

Hint:

IDs can be configured in the CLI config.

The rest of the options need to match the flags you used to pull the components.

Hint:

Visit the components push documentation for more information on this command.

And there you have it! Your production schema is now in sync with your development space.

Generate type definitions

Now that you are creating more components, you want to introduce type safety to reduce ambiguity and make your codebase easier to maintain.

You can generate type definitions from your Storyblok components schema by running:

storyblok types generate --space <space-id>

Migrations

In the context of the CLI, a migration is simply a script you generate and run to apply updates based on your transformation logic, usually to help you roll out schema changes without breaking existing content.

Let’s assume at this stage, that you have a fully hooked-up application up and running in your production environment.

Support schema updates

Now let’s say that you need to support colored text within the summary field of your articles. With the current definition, you cannot support this, as your summary field is of type text, where only plain text is supported. In order to support colored text within this field, you need to update the field type to rich text.

This is a plain text summary example. -> This is a rich text summary example.

In the above example, you can see the differences between the plaintext summary vs a rich text summary.

Required:

IMPORTANT! If you haven’t yet, create at least one sample article entry within your dev space. Make sure to populate all fields!

In order to support schema changes, like the one above, you cannot simply change the field type from text to rich text. Since you are handling the summary as a string and not as rich text, changing this will break your application.

In order to support this change without breaking changes, you need to:

  1. Create a new field that will serve as our rich text field
  2. Migrate our existing summary value to the new rich text field for all entries
  3. Make the change in our code to handle the new rich text field
  4. Delete the old summary field

This flow makes it so that the old field is not removed until you have made the necessary changes to your code to handle the new field, preventing breaking changes.

This may seem like a grueling task, but the CLI helps streamline the migration steps.

First, add the new field to your article content type. The new schema for the article block will be:

**Fields:**

- **Body**: Richtext
- **Summary**: Text
- **Summary_richtext**: Rich text

Next, generate a migration script for the desired block type. In this case, it will be the article block.

storyblok migrations generate article --space <source-space-id>
Hint:

Visit the migrations generate documentation for more information on this command.

A migration file is generated within the generated path:

<path>/
└── migrations/
    └── <space-id>/
        └── article.js

Update the transformation logic within the migration file to handle your specific use case. Since the new field is of type rich text, you must transform the existing string to a rich text object like so:

export default function (block) {
  block.summary_richtext = {
    type: "doc",
    content: [
      {
        type: "paragraph",
        attrs: {
          textAlign: null,
        },
        content: [
          {
            text: block.summary || "", // original summary field value
            type: "text",
          },
        ],
      },
    ],
  };
  return block;
}

See that the value of the paragraph text is the value of the original summary field.

After adding your transformation logic to your migration file, you need to actually run your migration on your existing entries. Scratch that…you need to test the migration first, to make sure that you don’t ruin all of your entries first.

Run the command:

storyblok migrations run article --space <space-id> --dry-run

The --dry-run flag specifies that the migration will run as a test and not apply the changes directly.

Hint:

Hint: Always dry-run a migration in your production space regardless if it was successful in lower environment spaces

After that has passed, you can run the migration, fully applying those transformations to your articles.

storyblok migrations run article --space <space-id> --publish published

This is the same command as before, but without the --dry-run flag. The --publish flag specifies that only already published entries should be published upon these transformations being made.

Learn:

he migrations run command can save a snapshot so you can roll back if you need to revert the changes.

Visit the migrations run and migrations rollback documentation for more information.

Sync content across spaces

Now that the schemas have been synced between spaces, you may want to sync the content from dev to prod. This consists of three different commands to pull the source content and three different commands to push the content to the target space.

One of the most powerful parts of this workflow is that you can mix and match these commands, so you only sync the type of content you need (datasources, assets, or stories), exactly when you need it.

First, pull all of the content from your target space with the following commands:

storyblok datasources pull --space <space-id>
storyblok assets pull --space <space-id>
storyblok stories pull --space <space-id> 

Then push your content to the target space with the following commands:

storyblok datasources pull --space <space-id> --from <source-space-id>
storyblok assets pull --space <space-id> --from <source-space-id>
storyblok stories pull --space <space-id> --from <source-space-id>
Hint:

For more information on these commands see the datasources, assets, and stories commands documentation.

Internationalization

Now, if you are going to support internationalization, you may need a static list of supported languages and locales within your codebase for version control, backups, and to use as your source of truth for available translations.

Generate a file with this information by running:

storyblok languages pull --space <space-id> 

The generated file contains the all enabled languages along with their language code, display name, and AI translation code like so:

{
  "default_lang_name": "English",
  "languages": [
    {
      "code": "de-at",
      "name": "German (Austria)",
      "ai_translation_code": "de"
    },
    {
      "code": "es-es",
      "name": "Spanish (Spain)",
      "ai_translation_code": "es"
    },
    {
      "code": "es-mx",
      "name": "Spanish (Mexico)",
      "ai_translation_code": "es"
    }
  ]
}

You can use the generated file to see which languages are enabled within a specific release, generating backups for the enabled languages, or even using it as a source of truth when handling locale switching within your front-end logic.

Hint:

Visit the languages pull documentation for more information on this command.

Conclusion

In this tutorial, you walked through a typical, boilerplate, development workflow and incorporated the use of the CLI where possible. From installing the CLI and using it to create a project, to leveraging the CLI to sync schemas and migrate content after changes were made, you demonstrated how the CLI can be useful in your day-to-day job as a Storyblok developer.

I hope this tutorial has been useful, and I hope the CLI has made your job even the slightest bit more joyful.