<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michael Kinkaid</title>
    <description>The latest articles on Forem by Michael Kinkaid (@meandmyrobot).</description>
    <link>https://forem.com/meandmyrobot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F273267%2Fb3c0b705-f120-47af-ac39-a9b9ffb753ca.png</url>
      <title>Forem: Michael Kinkaid</title>
      <link>https://forem.com/meandmyrobot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/meandmyrobot"/>
    <language>en</language>
    <item>
      <title>🎉 Weekly Win (27/2021)</title>
      <dc:creator>Michael Kinkaid</dc:creator>
      <pubDate>Wed, 30 Jun 2021 20:04:47 +0000</pubDate>
      <link>https://forem.com/kontent_ai/weekly-win-27-2021-3a2j</link>
      <guid>https://forem.com/kontent_ai/weekly-win-27-2021-3a2j</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Kentico Kontent support is fantastic. If you're stuck, reach out to them!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Did I Need Support?
&lt;/h2&gt;

&lt;p&gt;Web Spotlight.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Sorry—web what, now?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm glad you asked (otherwise this would have been a very short post indeed).&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless for Those with Heads
&lt;/h2&gt;

&lt;p&gt;Kentico Kontent is a 100% headless CMS. It ticks all the boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API-first&lt;/li&gt;
&lt;li&gt;Technology agnostic&lt;/li&gt;
&lt;li&gt;Channel agnostic&lt;/li&gt;
&lt;li&gt;Software-as-a-Service&lt;/li&gt;
&lt;li&gt;Has the word "&lt;em&gt;content&lt;/em&gt;" in its name, kinda.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All cool stuff, &lt;em&gt;but&lt;/em&gt;...&lt;/p&gt;

&lt;p&gt;If you're making websites with a headless CMS, then the fact you can't easily see what you're doing—outside of having the site open in another tab—can make updates a little tiring (a lot of clicking through nested content items to find that one bit of text you need to update).&lt;/p&gt;

&lt;p&gt;⚠️ Personal Opinion Alert: &lt;a href="https://dev.to/meandmyrobot/headless-cms-just-isn-t-good-enough-26a6"&gt;headless could be better&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kentico Kontent is Better
&lt;/h2&gt;

&lt;p&gt;A really great differentiator between Kentico Kontent and other headless CMS platforms is an app extension developed by Kentico called &lt;a href="https://webspotlight.kontent.ai/" rel="noopener noreferrer"&gt;Web Spotlight&lt;/a&gt;. Web Spotlight extends Kentico Kontent with visual page building.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbaenurt7wzhn0xfpz3td.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbaenurt7wzhn0xfpz3td.png" alt="Image shows the Web Spotlight user interface. A web page has edit buttons beside the text."&gt;&lt;/a&gt;&lt;/p&gt;
Click the edit button (pencil icon) beside the text in your web page to edit the content in your content inventory.



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;You can see the web page you're working on &lt;em&gt;within&lt;/em&gt; the Kentico Kontent app. This provides an easy visual way to find and edit content, versus digging through your content model. &lt;em&gt;Sweeeeet&lt;/em&gt; 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Let Me Play with the New Features—NOW!
&lt;/h2&gt;

&lt;p&gt;Web Spotlight just released a bunch of new features. The biggest of these is the &lt;strong&gt;Add Button&lt;/strong&gt;. This swanky feature allows editors to add components into the page they're working on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftl1vsjlridktlx9fvg38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftl1vsjlridktlx9fvg38.png" alt="Image shows the updated Web Spotlight UI. The web page has circular add buttons that editors can press to add components to pages."&gt;&lt;/a&gt;&lt;/p&gt;
New orange buttons! Click them to add or create components into your web page.



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Needless to say, once I spied the announcement on the update, I was eager to get the Add Button working on our site (Kontent + Gatsby). We're doing a bit of a refactor with the content model, so I'm already knee-deep in migration scripts. Where to start? The homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Read &lt;a href="https://github.com/Kentico/kontent-smart-link" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Make a feature branch&lt;/li&gt;
&lt;li&gt;Make sure I'm on the latest version of the Kentico Kontent Smart Link SDK&lt;/li&gt;
&lt;li&gt;Add some code (new data attributes)&lt;/li&gt;
&lt;li&gt;Paranoid check I'm playing in the Kentico Kontent development environment and not production 😉&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gatsby develop -S&lt;/code&gt; because I'm impatient and can't be bothered with ngrok at this exact moment&lt;/li&gt;
&lt;li&gt;Update the homepage preview URL in Kentico Kontent to &lt;code&gt;https://localhost:8000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;See the new Add Button (#&lt;em&gt;win&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Click on the new Add Button (heart now racing)&lt;/li&gt;
&lt;li&gt;And...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ySpxjJmsq9gsw/source.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ySpxjJmsq9gsw/source.gif" title="WOMP WOMP" alt="WOMP WOMP"&gt;&lt;/a&gt;&lt;/p&gt;
WOMP WOMP.



&lt;h2&gt;
  
  
  Sad Face 🙁
&lt;/h2&gt;

&lt;p&gt;Nope. Error. Something about "&lt;em&gt;missing data attributes&lt;/em&gt;"—even though they were right there in the code. RIGHT THERE. I should be looking pretty. All other Web Spotlight features are working. The Kentico Kontent SmartLink SDK is hooked up correctly. Why?!&lt;/p&gt;

&lt;h2&gt;
  
  
  Support to the Rescue
&lt;/h2&gt;

&lt;p&gt;I was about to stomp off to the kitchen to comfort eat when I spotted the wee-help-thingamajig in Kentico Kontent's UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4qxkplkqv3w3xrqtw3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4qxkplkqv3w3xrqtw3m.png" alt="Support Button"&gt;&lt;/a&gt;&lt;/p&gt;
Talk to—and get support from—a REAL person.



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Yes. Yes, I do have questions. I quickly typed up the issue I was experiencing and within a minute a very polite and capable chap called Keith started helping me through the issue.&lt;/p&gt;

&lt;p&gt;I explained that I was trying to set up the Add Button with a Linked Item Element (supported). We talked through the model, and I mentioned that the Linked Item Element was coming from a Content Snippet, and that this was the only thing that stood out as maybe being unique to what I was trying to do.&lt;/p&gt;

&lt;p&gt;He agreed and said he'd go pester the developers to ensure that the use case of having the Add Button configured to a content element coming from a Content Snippet is actually supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Win?
&lt;/h2&gt;

&lt;p&gt;It wasn't supported. Wait, isn't that a #&lt;em&gt;fail&lt;/em&gt; and not a #&lt;em&gt;win&lt;/em&gt;? Isn't now the time to slink off into the shadows and just avoid using the Add Button on the homepage?&lt;/p&gt;

&lt;p&gt;Absolutely not! The #&lt;em&gt;win&lt;/em&gt; here is the support provided by Kentico Kontent. In less than a week the developers had coded, tested, and deployed a fix. Not only that, but Keith kept me up to date the entire time on their progress via the chat support feature in Kentico Kontent.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Works!
&lt;/h2&gt;

&lt;p&gt;Behold. The first Add Button of many.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/G71NockBqdo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Adding a new component to the homepage. Refresh status and quick update thanks to Gatsby and incremental builds.



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Onwards with the refactoring!&lt;/p&gt;

</description>
      <category>win</category>
      <category>gatsby</category>
      <category>development</category>
      <category>lessonlearned</category>
    </item>
    <item>
      <title>Headless CMS just isn't good enough...</title>
      <dc:creator>Michael Kinkaid</dc:creator>
      <pubDate>Mon, 17 Aug 2020 12:35:15 +0000</pubDate>
      <link>https://forem.com/meandmyrobot/headless-cms-just-isn-t-good-enough-26a6</link>
      <guid>https://forem.com/meandmyrobot/headless-cms-just-isn-t-good-enough-26a6</guid>
      <description>&lt;p&gt;OK, so before I get accused of clickbait (&lt;em&gt;perhaps a fair accusation&lt;/em&gt;), let's start by finishing up that intentionally provocative title: Headless CMS just isn't good enough for - (🥁 drumroll)- &lt;em&gt;those of us who have heads&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🤯
&lt;/h1&gt;

&lt;p&gt;Still too vague and sensational? Concerned this post hasn't really kicked off and I've &lt;em&gt;already&lt;/em&gt; used 2 emojis? OK - to be specific - headless CMS just isn't good enough for the actual people who log in to the platform to create and publish content &lt;strong&gt;for a website&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem? Well, whereas authoring content on a headless platform is generally a joyful experience, bringing that content all together to present it on a channel; e.g., creating a web page for your site, can be a little tricky.&lt;/p&gt;

&lt;p&gt;This is because, to create that new web page, you have to deal with a whole bunch of very specific channel "&lt;em&gt;things&lt;/em&gt;" like pages, templates, components, navigation, and URLs. This can be challenging on a headless CMS because, with the exception of a basic preview feature, they don't typically give great visual feedback to help with page composition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;It's hard to see where you're going without a head&lt;/em&gt;&lt;/strong&gt;: The other &lt;em&gt;just as obnoxious&lt;/em&gt; working title for this post...&lt;/p&gt;

&lt;p&gt;This lack of visual feedback and contextual support can be frustrating for content creators (editors and marketers) to deal with. They're going to want as much control and flexibility with the channel as possible. If, like in this example, that channel is web, then they're going to want to work within that context - creating and managing web pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Good Ol' Days
&lt;/h2&gt;

&lt;p&gt;If you're a content creator who’s worked with traditional CMS platforms, then this lack of visual feedback when it comes to page composition is likely the first thing you'll notice when switching to a headless CMS.&lt;/p&gt;

&lt;p&gt;Not only do traditional CMS platforms allow content creators to work with structured content, but they typically provide really rich editing experiences specific to making an actual website. You get features like visual page composition, drag and drop, a way to see and manage your site structure. You're managing content &lt;strong&gt;within the context&lt;/strong&gt; of how it's actually going to be consumed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yUpVTVdI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/IeDJrX2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yUpVTVdI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/IeDJrX2.png" alt="Image shows how a traditional CMS provides visual page composition with features like page templates and components" title="Traditional CMS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Somewhat Established "Newness"
&lt;/h2&gt;

&lt;p&gt;In comparison, a headless CMS platform focuses on content authoring and making that content available to practically anything that can talk to its API. How much it knows or cares about the channels you present your content on is entirely up to whoever is designing the content model.&lt;/p&gt;

&lt;p&gt;If one of your channels is a website then a common approach is to add content types to your model for all those website elements your editors are going to need to manage; e.g., &lt;strong&gt;Page&lt;/strong&gt;, &lt;strong&gt;Navigation&lt;/strong&gt; items, components like a &lt;strong&gt;Banner&lt;/strong&gt;, and &lt;strong&gt;Call to Action&lt;/strong&gt;. This provides the flexibility content creators want - but page composition is a little bit more &lt;em&gt;abstract&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AVKQ_fN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/5l2UIyR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AVKQ_fN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/5l2UIyR.png" alt="Image shows how a headless CMS doesn't provide visual page composition but rather uses fields so editors can build pages by making content relationships" title="Headless CMS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on the number of moving parts in your model, creating and connecting all these bits of content can start to feel like you're trapped in Christopher Nolan's &lt;em&gt;Inception&lt;/em&gt;. Here's an example of a dream within a dream within a dream...&lt;/p&gt;

&lt;p&gt;I want to add a new &lt;strong&gt;Sponsor Call to Action&lt;/strong&gt; (CTA) to the homepage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Create a new Sponsor content item for the info specific to the new sponsor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Create the new Sponsor CTA content item to manage actions, URLs, web stuff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Associate the new Sponsor to the new Sponsor CTA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Find the Sponsor List component that we use to group our Sponsor CTAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Add the Sponsor CTA to the Sponsor List component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Find and open the homepage content item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Click preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Finally see what you've created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Take a nap&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The solution to all this elaborate content juggling? Well, that's pretty obvious.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Give up on all the benefits of headless CMS and go back to using a more traditional CMS.&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  The End.
&lt;/h2&gt;




&lt;p&gt;OK, so that's a knee-jerk reaction rather than a solution. We want all the benefits of a headless CMS. We'd also like more visual feedback when it comes to dealing with the channel our content will be available on. The solution is to bring all these things together.&lt;/p&gt;

&lt;p&gt;How might that work? How do we do this without reverting our headless CMS back into a tool that only knows how to make a website?&lt;/p&gt;

&lt;p&gt;Thankfully, answering these questions doesn't have to be a thought experiment as one headless CMS platform - &lt;a href="https://kontent.ai/?utm_source=medium_content_modelling_series_part1&amp;amp;utm_medium=mvp_94301&amp;amp;utm_campaign=extended_trial"&gt;Kentico Kontent&lt;/a&gt; - just announced how they intend to do it. Their solution is to provide an &lt;strong&gt;optional&lt;/strong&gt; add-on to their headless CMS. This add-on is called &lt;a href="https://webspotlight.kontent.ai/"&gt;Web Spotlight&lt;/a&gt; and it's a tool specifically tailored to helping content creators manage the web channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4o2n6oYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/MuujAlM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4o2n6oYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/MuujAlM.png" alt="dd" title="dd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the preview above it's clear Kentico Kontent is taking the frustrations of content creators head on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Having difficulty finding the page you need to update?&lt;/em&gt; You can now manage the site structure using a content tree (shown on the left).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Able to see content you need to update in the preview, but have no idea where that content item lives in your inventory?&lt;/em&gt; You can now just click on the content and instantly jump into editing mode (shown on the right).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even better: unlike a traditional CMS that typically locks you into coding your entire site in their preferred language, Web Spotlight uses an API so you're still free to use any technology stack. You just have to integrate with the API when rendering your preview.&lt;/p&gt;

&lt;p&gt;The approach of releasing Web Spotlight as an optional add-on highlights how Kentico Kontent intend to evolve the whole headless paradigm &lt;strong&gt;without breaking it&lt;/strong&gt;. If you're creating content for a website and want additional support, then Kentico's Web Spotlight provides a tool designed to enhance editing for that channel.&lt;/p&gt;

&lt;p&gt;Not creating a website, or don't need the additional context of Web Spotlight? Then you don't have to use it.&lt;/p&gt;

&lt;p&gt;It's easy to imagine how Web Spotlight could evolve to provide even more web-focused enhancements for content creators (easier management of channel-specific content types, drag and drop components, etc.).&lt;/p&gt;

&lt;p&gt;And why stop at web? Now that a model of &lt;em&gt;optional channel editors&lt;/em&gt; is established, Kentico Kontent could provide add-ons to enhance working with other channels or integrations.&lt;/p&gt;

&lt;p&gt;As with any new feature, users will ultimately decide the fate of this approach. It'll be interesting to see if other headless CMS platforms adopt something similar. There are already a few out there with visual editors (e.g., StoryBlok), but &lt;a href="https://webspotlight.kontent.ai/"&gt;Web Spotlight&lt;/a&gt; is the first I've seen as a separate add-on that extends the headless CMS. This separation feels like the best way to allow both parts to evolve independently.&lt;/p&gt;

&lt;p&gt;For an extended trial of Kentico Kontent, go use &lt;a href="https://kontent.ai/?utm_source=medium_content_modelling_series_part1&amp;amp;utm_medium=mvp_94301&amp;amp;utm_campaign=extended_trial"&gt;this magical link&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>headlesscms</category>
      <category>cms</category>
      <category>kenticokontent</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Migrating Model Changes with the Kentico Kontent CLI</title>
      <dc:creator>Michael Kinkaid</dc:creator>
      <pubDate>Wed, 01 Jul 2020 11:55:03 +0000</pubDate>
      <link>https://forem.com/meandmyrobot/migrating-model-changes-with-the-kentico-kontent-cli-2iba</link>
      <guid>https://forem.com/meandmyrobot/migrating-model-changes-with-the-kentico-kontent-cli-2iba</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MumYw14O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a4qwf26v3vt38le58hdd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MumYw14O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a4qwf26v3vt38le58hdd.jpg" alt="Image of migrating birds" title="Photo by Pille Kirsi from Pexels"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kontent.ai/?utm_source=medium_content_modelling_series_part1&amp;amp;utm_medium=mvp_94301&amp;amp;utm_campaign=extended_trial"&gt;Kentico Kontent&lt;/a&gt; has a &lt;a href="https://github.com/Kentico/kontent-cli"&gt;CLI&lt;/a&gt; that you can use to manage your content model - &lt;strong&gt;using code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not only can you build your model using code, but you can also migrate your model changes across any environments that you've set up in Kentico Kontent i.e. Development, QA, UAT, Production etc.&lt;/p&gt;

&lt;p&gt;In this post we're going to use the migrations feature of the CLI to create a content model &lt;em&gt;from scratch&lt;/em&gt;. We won't be designing the model in Kentico Kontent's user interface. Instead, we'll be doing everything by code.&lt;/p&gt;

&lt;p&gt;We'll need a blank project to work with - &lt;a href="https://app.kontent.ai/projects"&gt;so go over to Kontent&lt;/a&gt; and create a new one. Don't go modelling anything, though 😁.&lt;/p&gt;

&lt;p&gt;If you want to grab the final code then clone the following &lt;a href="https://github.com/meandmyrobot/kontent-cli-migration"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before you start, make sure you have &lt;strong&gt;Node 10+&lt;/strong&gt; and &lt;strong&gt;npm 6+&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - Set up a Migration Project
&lt;/h2&gt;

&lt;p&gt;Create a folder wherever you set up your projects. Open a new command window or terminal at that folder location. Kick off a new project using npm or yarn. I'm going to use npm, so run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We're going to need a few dependencies in order to run our scripts. These are:&lt;/p&gt;

&lt;h3&gt;
  
  
  RxJS
&lt;/h3&gt;

&lt;p&gt;The CLI uses the &lt;a href="https://docs.kontent.ai/reference/management-api-v2"&gt;Kentico Kontent Management API (v2)&lt;/a&gt;. This has a peer dependency on RxJS, so let's install this before we add the CLI. I've heard rumour this dependency &lt;em&gt;may&lt;/em&gt; be going away some time in the future. That, or I've been having weird dreams again. Comment below if I'm horribly wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i rxjs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Kentico Kontent CLI
&lt;/h3&gt;

&lt;p&gt;Next, let's go grab the CLI. This does support global installation (add the '-g' flag to the line below). I've been installing it locally, given the RxJS dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @kentico/kontent-cli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Dotenv
&lt;/h3&gt;

&lt;p&gt;The migration process will use project keys from Kentico Kontent. Dotenv allows us to store secret API keys as environment variables, which saves you from putting these directly into your code. Be sure to keep this information out of source control as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i dotenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 - Grab your Project Keys
&lt;/h2&gt;

&lt;p&gt;OK, so we do need to bounce into Kentico Kontent to get those secret API keys. Open the blank project you created and go to &lt;strong&gt;Project Settings&lt;/strong&gt; (the cog icon in the menu). When we create a new project, Kentico Kontent creates a single &lt;strong&gt;Production&lt;/strong&gt; environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FXqzyUn9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/3JNgypr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FXqzyUn9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/3JNgypr.jpg" alt="Kentico Kontent Production Environment" title="Kentico Kontent Production Environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's grab the settings for this environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Product ID&lt;/li&gt;
&lt;li&gt;The Management API Key (make sure to activate it)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vg-Zqt_Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/ex1mfRz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vg-Zqt_Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/ex1mfRz.jpg" alt="Kentico Kontent Project Settings" title="Kentico Kontent Project Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's use the Kentico Kontent CLI to save these settings into our project. Add in your unique settings to the line below and run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent environment add &lt;span class="nt"&gt;--name&lt;/span&gt; PROD &lt;span class="nt"&gt;--project-id&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;YOUR_PROJECT_ID&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--api-key&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;YOUR_MANAGAMENT_API_KEY&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;--name&lt;/strong&gt; parameter can be anything you want. You'll use this name ("PROD" in our example) to target the environment you want to run your migrations on.&lt;/p&gt;

&lt;p&gt;If this has worked as intended then the Kentico Kontent CLI will have created a file called &lt;em&gt;.environments.json&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PROD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;THE_PROJECT_ID_YOU_ENTERED&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"apiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;THE_MANAGAMENT_API_KEY_YOU_ENTERED&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can repeat &lt;strong&gt;Step 2&lt;/strong&gt; for each environment that you set up on a project. We don't &lt;em&gt;have to&lt;/em&gt; do this now. Because we're starting from a blank project, our Production environment is enough. However, if this was a &lt;em&gt;real gig&lt;/em&gt; our content pipeline could have multiple environments, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development to QA to UAT to Production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Environments are managed under &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Environments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NR9S0y6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/zS6rvxz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NR9S0y6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/zS6rvxz.jpg" alt="Clone an existing environment" title="Clone an existing environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click &lt;strong&gt;Clone&lt;/strong&gt;, Kentico Kontent will copy &lt;em&gt;everything&lt;/em&gt; from the selected environment into a new environment (the content model and all content items). The new environment will have &lt;strong&gt;completely new settings&lt;/strong&gt; (Project ID and Management API Key), which is why you would repeat the step to save those settings into your &lt;em&gt;.environments.json&lt;/em&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Add a New Migration Script
&lt;/h2&gt;

&lt;p&gt;The Kentico Kontent CLI has a handy command to get started with migration scripts. Run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration add &lt;span class="nt"&gt;--name&lt;/span&gt; 01_create_album_review_content_type
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This creates a new JavaScript migrations file (with the catchy name of &lt;em&gt;01_create_album_review_content_type.js&lt;/em&gt;). The module has the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can have multiple migration scripts. Depending on what you're doing to your model, you'll likely have an order you want to run these in. That execution sequence is controlled through the &lt;strong&gt;order&lt;/strong&gt; property.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;run&lt;/strong&gt; function is where you put the migration code that you want to execute on your content model. The parameter getting passed here is an instance of the Kentico Kontent Management API client. As we'll see, this client allows you to do some pretty cool things to your content model and all your content items.  &lt;/p&gt;

&lt;p&gt;Running the command also created a folder called &lt;strong&gt;Migrations&lt;/strong&gt; (within your project folder). This is where Kentico Kontent put the script. All your migration scripts need to be in a folder called &lt;em&gt;Migrations&lt;/em&gt;, otherwise an error will be thrown 🔥🔥🔥.&lt;/p&gt;

&lt;p&gt;Open the project up in your favourite editor. It's time to start writing some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Creating Content Types
&lt;/h2&gt;

&lt;p&gt;As the name of our first migration script would suggest (01_create_album_review_content_type.js), we're going to create a new content type called &lt;strong&gt;Album Review&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This content type is going to start with the following fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title&lt;/strong&gt; (text content element)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Album name&lt;/strong&gt; (text content element, required)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artist&lt;/strong&gt; (text content element, required)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt; (rich text content element)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update your migration script with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BuildAlbumReviewTypeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BuildAlbumReviewTypeData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Album Review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;album_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Album Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;album_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;is_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Artist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;artist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;s_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rich_text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;run&lt;/strong&gt; function shows the client call to create a new content type. We're defining the structure of our Album Review content type in &lt;strong&gt;BuildAlbumReviewTypeData&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To run this migration script ("01_create_album_review_content_type") on the default production environment (which we registered as "Prod"), execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration run &lt;span class="nt"&gt;--environment&lt;/span&gt; PROD &lt;span class="nt"&gt;-n&lt;/span&gt; 01_create_album_review_content_type
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the migration ran successfully then you should see the following in your output:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The "01_create_album_review_content_type.js" migration on a project with ID "" executed successfully.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you jump into Kentico Kontent and go to the &lt;em&gt;Content models&lt;/em&gt;, then you'll see the new content type:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly9XCqaK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/AlTchiL.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ly9XCqaK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/AlTchiL.jpg" alt="New content type added to the model" title="New content type added to the model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on the content type to open it up, then you'll see the structure we added using our migration script:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1C9muwKv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/xlG93IL.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1C9muwKv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/xlG93IL.jpg" alt="The structure of our content type" title="The structure of our content type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll also notice that the Kentico Kontent CLI has created a &lt;strong&gt;status.json&lt;/strong&gt; file at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;YOUR_PROJECT_ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01_create_album_review_content_type.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-29T22:15:10.115Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As the name suggests, this file keeps track of the status returned from your migration scripts. This file will get updated as you run future scripts.&lt;/p&gt;

&lt;p&gt;Let's create one more content type so that we'll have a little more in our model to play with. Create a second file in the migrations folder called &lt;em&gt;02_create_reviewer_content_type.js&lt;/em&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BuildReviewerTypeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BuildReviewerTypeData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;first_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;is_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Last Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;last_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;is_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Twitter Handle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twitter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The migration script will create a new &lt;strong&gt;Reviewer&lt;/strong&gt; content type that we're going to use in a relationship with our &lt;strong&gt;Album Review&lt;/strong&gt; content type.&lt;/p&gt;

&lt;p&gt;Run this migration with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration run &lt;span class="nt"&gt;--environment&lt;/span&gt; PROD &lt;span class="nt"&gt;-n&lt;/span&gt; 02_create_reviewer_content_type
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can run migrations individually or as a batch. Use the following Kentico Kontent CLI command to run &lt;strong&gt;all&lt;/strong&gt; your migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration run &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--environment&lt;/span&gt; PROD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The migration process will skip any migration that it has already processed. You'll see this in the output in your command window / terminal:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Skipping already executed migration 01_create_album_review_content_type.js&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 - Updating a Content Type
&lt;/h2&gt;

&lt;p&gt;As your model extends, you're going to want to update content types that you've already created. This can also be done in a migration script.&lt;/p&gt;

&lt;p&gt;Now that we've got a Reviewer content type, we should create a content element (fancy pants term for a &lt;em&gt;field&lt;/em&gt;) in our Album Review content type so we can link these two; i.e., an Album Review will be written by one Reviewer.&lt;/p&gt;

&lt;p&gt;Create a new migration script in the migration folder called &lt;em&gt;03_add_reviewer_linked_item.js&lt;/em&gt;. Add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addInto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/elements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;codename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modular_content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modifyContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byTypeCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;album_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you look first at the API call we're building, you'll see we're modifying the Album review content type (which we target through the code name). Our modification is an array of operations that contain content type data.&lt;/p&gt;

&lt;p&gt;We only have one operation defined in this call. Our operation is going to add (&lt;strong&gt;addInto&lt;/strong&gt;) a new modular content element. "Modular Content" is a legacy API name. You'll see it called a "Linked Item" in the user interface.&lt;/p&gt;

&lt;p&gt;Run your migrations again. This time, try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration run &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--environment&lt;/span&gt; PROD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should notice that the first two migrations are skipped, and that only the third runs.&lt;/p&gt;

&lt;p&gt;If we jump into Kentico Kontent and look at the Album Review content type then at the bottom we will see our new field:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ylSXvH_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/oLcurnq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ylSXvH_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/oLcurnq.jpg" alt="New Reviewer field added to the Album Review content type" title="New Reviewer field added to the Album Review content type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 - Configuring Relationships
&lt;/h2&gt;

&lt;p&gt;If you're familiar with Kentico Kontent then you'll know that the Linked Item content element offers a &lt;em&gt;lot&lt;/em&gt; in terms of handy configuration that will make editors' lives easier - and protect our model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XaiXUSFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/YJtgU5m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XaiXUSFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/YJtgU5m.jpg" alt="A Linked Item content element configured to limit relationships" title="A Linked Item content element configured to limit relationships]"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above is from another project. You can see that the Linked Item content element is required, and that it can have only one relationship to a &lt;strong&gt;Widget-Logo Grid&lt;/strong&gt; content item.&lt;/p&gt;

&lt;p&gt;The Reviewer content element &lt;em&gt;should&lt;/em&gt; only allow an association to one Reviewer. However, that's not how things are currently set up in our content model. An editor could link an Album Review to any number of different content types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n97GiEvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/XGEt7a1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n97GiEvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/XGEt7a1.jpg" alt="The Reviewer Linked Item content element has no configuration" title="The Reviewer Linked Item content element has no configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we could've set the following configuration in our previous step, but I wanted to show you how you can make deeper-level edits and replace or add new configuration to content elements that are already part of a content type.&lt;/p&gt;

&lt;p&gt;Add a new migration script in the Migrations folder called &lt;em&gt;04_update_reviewer_linked_item.js&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;getReviewerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byTypeCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;reviewerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getReviewerId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/elements/codename:reviewer/item_count_limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exactly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/elements/codename:reviewer/allowed_content_types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reviewerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modifyContentType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byTypeCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;album_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are a few things of note in this migration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're applying multiple operations to the Album Review content type Reviewer content element. Or, in other words, we're doing a bunch of stuff to the Reviewer field 😎. We set the &lt;em&gt;item_count_limit&lt;/em&gt; to '1' and set the &lt;em&gt;allowed_content_types&lt;/em&gt; to our Reviewer content type.&lt;/li&gt;
&lt;li&gt;In order to create the relationship we need to use the ID of the Reviewer content type. We don't have this - but we can ask for it. This is done in the function &lt;strong&gt;getReviewerId&lt;/strong&gt;, which uses the API to query for the Reviewer content type data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kontent migration run &lt;span class="nt"&gt;--environment&lt;/span&gt; PROD &lt;span class="nt"&gt;-n&lt;/span&gt; 04_update_reviewer_linked_item
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you pop over to Kentico Kontent and check out the Album Review content type, you'll see that the Reviewer content element now has the configuration we need to keep our model nice and tidy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F9obHdwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/y4RPNU5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F9obHdwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/y4RPNU5.jpg" alt="New Reviewer field added to the Album Review content type" title="New Reviewer field added to the Album Review content type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;A lot can be done with the Kontent CLI and Kontent Management API. For me, the next step is definitely doing more homework on managing changes across environments.&lt;/p&gt;

&lt;p&gt;Managing your model through code requires you to understand the structure Kentico Kontent uses to represent your content model and content items.&lt;/p&gt;

&lt;p&gt;For example, when it came to setting the allowed content types (&lt;em&gt;allowed_content_types&lt;/em&gt;) to &lt;strong&gt;Reviewer&lt;/strong&gt; (&lt;em&gt;a GUID&lt;/em&gt;), how did I know the name of the property and the fact that a GUID was required?&lt;/p&gt;

&lt;p&gt;This is where querying the &lt;a href="https://docs.kontent.ai/reference/delivery-api"&gt;Delivery API&lt;/a&gt; or &lt;a href="https://docs.kontent.ai/reference/management-api-v2"&gt;Management API&lt;/a&gt; with a tool like &lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt; comes &lt;strong&gt;super&lt;/strong&gt; in handy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GoXjmQUM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/UtinYKF.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GoXjmQUM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/UtinYKF.jpg" alt="Checking out the structure of a content type using Postman" title="Checking out the structure of a content type using Postman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For that matter, how did I know the correct format for the &lt;em&gt;path&lt;/em&gt; property (&lt;em&gt;elements/codename:reviewer/allowed_content_types&lt;/em&gt;)? For this type of insight, you really need to check out the excellent &lt;a href="https://docs.kontent.ai/reference/management-api-v2#operation/modify-a-content-type"&gt;Kentico Kontent documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Interested in another example that also includes TypeScript? Check out Kentico Kontent's own &lt;a href="https://github.com/Kentico/kontent-migrations-boilerplate"&gt;boilerplate project&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Have fun migrating your content model changes!&lt;/p&gt;

&lt;p&gt;Photo at the top by &lt;a href="https://www.pexels.com/@pille-kirsi-222198?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels"&gt;Pille Kirsi&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>headless</category>
      <category>kontent</category>
    </item>
  </channel>
</rss>
