loading...

Basic space setup and CRUD with Contentful CLI for development

alexalexyang profile image alex ・5 min read

We love CLI's. They help us automate things. But Contentful's CLI documentation is scattered all over the place. Here, I walk you through the basic bits needed for development. This is not comprehensive, but it should help get you going.

Basic setup

Get the CLI

Brew, yarn, or npm. Eg: npm install -g contentful-cli

Ref: Contentful CLI git repo.

Get your CMA

Log into Contentful. Go to Settings. Go to Content management tokens. Click on "Generate personal token". Copy the token somewhere (important because you'll never see it on the site ever again).

Login on CLI

Run this in shell: contentful login --management-token YOUR_CONTENT_MANAGEMENT_TOKEN. It'll tell you it saved your access token in .contentfulrc.json in your present working directory.

Ref: Contentful CLI - Documentation

Create a space

contentful space create --name 'Your Space Name'. You might need --management-token and pass in your content management key. And --default-locale if you want to set a default locale that isn't us-En.

If successful, it'll show you your space name alongside your space-id. Copy the space-id down somewhere.

Ref: Contentful CLI - Space command

Remember your space

contentful space use -s YOUR_SPACE_ID. This lets the CLI remember which space you're working in so you don't have to pass in --space-id all the time.

Ref: Contentful CLI - space use command

Logout

Don't logout now. But if you want to: contentful logout. It'll delete the .contentfulrc.json.

Set up space with migrations

Now it's time to create content types (like database tables) and their fields in your space. The CLI uses a migration script written in JavaScript to do this. It's not immediately clear from the docs if we can write this in other languages and I don't have time to check right now.

In general, most of the info is here: contentful-migration - content model migration tool. But this is a large chunk of everything.

You'll need to look at the data model too, to figure out what you can use to make, say, a blog post title and a post body: Contentful data model

Here is a more specific way to set things up on a basic level: The right way to migrate your content using the Contentful Migration CLI

In brief, your script should look something like this, taken directly from the link above:

module.exports = function(migration) {
  const author = migration.createContentType('author')
  author
    .name('Author')
    .description('Author of a blog post')

  author
    .createField('fullName')
    .name('Full name')
    .type('Symbol')
    .required(true)

  author
    .createField('twitter')
    .name('Twitter')
    .type('Symbol')
    .validations([
      { "unique": true },
      { "regexp": 
        { "pattern": "^\\w[\\w.-]*@([\\w-]+\\.)+[\\w-]+$" }
      }
    ])

  const blogPost = migration.editContentType('blogPost')
  blogPost.createField('author')
    .name('Author')
    .type('Link')
    .linkType('Entry')
    .validations([
      { "linkContentType": ["author"] }
    ])
};

Put it in migrations/01-add-author.js and run contentful space migration migrations/01-add-author.js.

If you didn't run contentful space use -s YOUR_SPACE_ID earlier, you might have to pass in --space-id YOUR_SPACE_ID too.

I'll paste my whole blog setup migration script at the end of this article so you can have something a little meatier to look at.

Delete content types and fields

This is our development phase so we CRUD everything all the time. We've created content types. But how to delete? It's probably possible to use native commands to delete stuff but, again, it's not immediately clear from the docs how to do this.

I'm in a rush and I'm too tired from reading docs all day so I'll cheat a little and use this library called contentful-clean-space. Install it: npm install -g contentful-clean-space.

Then, run: contentful-clean-space --space-id YOUR_SPACE_ID --accesstoken YOUR_CONTENT_MANAGEMENT_TOKEN --content-types. This deletes all entries/records and content types too. Check out their repo for a bit more info.

(Reads of entries are through the Contentful CDA and updates are done later through the CMA)

The end

I think that's it.

References

CLI docs: basic use of CLI like login and how to do a migration. But does not include what a migration file looks like, and how to add content types and fields to them.

CLI migration docs: details on how to write the migration script.

Data model: tells you about what fields can be added to your migration script, but has no info on what the script itself looks like.

Automated Contentful migrations: a couple of examples for migration scripts worth skimming through for a start.

The right way to migrate your content using the Contentful Migration CLI: more useful details for migration scripts, like how to link from one content type to another:

const blogPost = migration.editContentType('blogPost')
  blogPost.createField('author')
    .name('Author')
    .type('Link')
    .linkType('Entry')

    // Isolates link to only the "author" content type.
    .validations([
      { "linkContentType": ["author"] }
    ])
};

How to do validations on items({}). Basically:

blogPost.createField("categories")
    .name("Categories")
    .required(false)
    .type('Array')
    .items({
        type: 'Link',
        linkType: "Entry",

        // Right here.
        validations: [{
            linkContentType: [
                "categories"
            ],
        }]

    })

Contentful Migration Cheat Sheet: clues you in on how to do some of these fields, and also nice reminders.

Delete content types and fields from CLI: contentful-clean-space

My blog site migration script

It's not the best but it's a good start. It sets up content types and fields for generic pages, blog posts, and default site settings.

module.exports = (migration, context) => {

    // BLOG POST CONTENT TYPE

    const blogPost = migration.createContentType("blogPost")
        .name("Blog Post")
        .description("Blog post model")
        .displayField("title")

    blogPost.createField("title")
        .name("Title")
        .type("Symbol")
        .required(false)

    blogPost.createField("body")
        .name("Body")
        .type("Text")
        .required(false)

    blogPost.createField("author")
        .name("Author name")
        .type("Symbol")
        .required(false)

    blogPost.createField("datetime")
        .name("Datetime")
        .type("Date")
        .required(false)

    blogPost.createField("categories")
        .name("Categories")
        .type('Array')
        .items({
            type: 'Link',
            linkType: "Entry",
            validations: [{
                linkContentType: [
                    "categories"
                ],
            }]
        })
        .required(false)

    blogPost.createField("tags")
        .name("Tags")
        .type("Array")
        .items({ "type": "Symbol" })


    blogPost.createField("featuredImage")
        .name("Featured image")
        .type("Link")
        .linkType("Asset")
        .required(false)


    // CATEGORIES CONTENT TYPE

    const categories = migration.createContentType('categories')
        .name('Categories')
        .description('Categories for blog posts')
        .displayField("category")

    categories.createField('category')
        .name('Category')
        .type('Symbol')
        .required(true)

    categories.createField('slug')
        .name('URL Slug')
        .type('Symbol')
        .validations([{ "unique": true }])
        .required(false)

    categories.createField('featuredImage')
        .name('Featured image')
        .type('Link')
        .linkType('Asset')
        .required(false)

    categories.createField('description')
        .name('Description')
        .type('Text')
        .required(false)


    // PAGE CONTENT TYPE

    const page = migration.createContentType("page")
        .name("Page")
        .description("Page model")
        .displayField("title")

    page.createField("title")
        .name("Title")
        .type("Symbol")
        .required(false)

    page.createField("body")
        .name("Body")
        .type("Text")
        .required(false)

    page.createField("featuredImage")
        .name("Featured image")
        .type("Link")
        .linkType("Asset")
        .required(false)

    // SITE SETTINGS

    const siteSettings = migration.createContentType("siteSettings")
        .name("Site settings")
        .description("Site Settings model")
        .displayField("siteName")

    siteSettings.createField("siteName")
        .name("Site name")
        .type("Symbol")
        .required(false)

    siteSettings.createField("author")
        .name("Author")
        .type("Symbol")
        .required(false)

    siteSettings.createField("address")
        .name("Address")
        .type("Symbol")
        .required(false)

    siteSettings.createField("phoneNumber")
        .name("Phone number")
        .type("Symbol")
        .required(false)

    siteSettings.createField("email")
        .name("Email")
        .type("Symbol")
        .required(false)

    siteSettings.createField("facebookLink")
        .name("Facebook link")
        .type("Symbol")
        .required(false)

    siteSettings.createField("twitterLink")
        .name("Twitter link")
        .type("Symbol")
        .required(false)

    siteSettings.createField("instagramLink")
        .name("Instagram link")
        .type("Symbol")
        .required(false)

    siteSettings.createField("defaultImage")
        .name("Default Image")
        .type("Link")
        .linkType("Asset")
        .required(false)
}

Posted on by:

alexalexyang profile

alex

@alexalexyang

Hey there. What're you doing standing in the doorway, you silly little bunting. Come right in. I've got coffee and cookies and pastries and potatoes. All kinds of good stuff. Let's chill.

Discussion

pic
Editor guide