Designly Blog

React / Contentful - Create Entries with Linked Objects

React / Contentful - Create Entries with Linked Objects

Posted in Cloud Computing by Jay Simons
Published on May 26, 2022

Contentful has become my favorite Headless CMS. I use it to generate static web pages, this blog, and storing other forms of data, such as user profiles.

Like many other CMSs, Contentful is an object-based data storage system. Contentful calls objects Content Models. Content models can have fields of several types, such as Short Text, Long Text (Rich Text or Markdown), Number, Boolean, etc. Each record of a particular content model is called an Entry, and content model fields can be of a type that references other content model entries.

A simple example would be if one had a content model called Blog Post, and one called Author. The field Author in Blog Post would reference an entry of content type Author. Reference fields can either reference a single entry or many.

Because I use Contenful primarily with React / Next.JS apps, this tutorial will focus on programmatically creating entries with linked objects using Contentful's Content Management API (CMA) Javscript SDK. I will also assume you are already using Contentful's Delivery API in your React project.

Install the JavaScript SDK

To add , delete or modify entries, you'll need to install the Contentful Management Javascript SDK. To do so, simple run the following command in your project root directory:

npm i contentful-management

If you don't already have one, you'll need to generate a content management token under API keys in your Contentful settings:

Generate a Content Management Token
Generate a Content Management Token

Next, add the following environment variable to your env.local file:

CONTENTFUL_MANAGEMENT_TOKEN=CFPAT-W-vQPcKWMV5GhXV1CAVWDEazy-1yRpnN4H3cqWFGfXU
CONTENTFUL_SPACE_ID=<YOUR CONTENTFUL SPACE ID>

Create Your Library Functions

Now that our environment is setup, we can now create our library functions for managing our content:

/** Function for creating JSON link objects **/

export function createLinkObject(id) {
    const link = `{
        "en-US": {
            "sys": {
                "type": "Link",
                "linkType": "Entry",
                "id": "${id}"
            }
        }
      }`;
    return JSON.parse(link);
}

The above function creates the JSON entity that CMA expects for linking objects. You simply only need to feed it the entry ID you wish to link and it returns a JSON object.

To see how this works, let's now create a function for adding an entry:

import { createLinkObject } from '/path/to/lib/file'

const contentful = require('contentful-management')

function slugify(str) {
    /** Convert string to URL-safe slug */
    return str
        .toString()                   // Cast to string (optional)
        .normalize('NFKD')            // The normalize() using NFKD method returns the Unicode Normalization Form of a given string.
        .toLowerCase()                // Convert the string to lowercase letters
        .trim()                       // Remove whitespace from both sides of a string (optional)
        .replace(/\s+/g, '-')         // Replace spaces with -
        .replace(/[^\w\-]+/g, '')     // Remove all non-word chars
        .replace(/\-\-+/g, '-');      // Replace multiple - with single -
}

export async function createPost({
    title,       // Post title (string)
    user,        // User who posted (entry object)
    category,    // Category (entry object)
    content      // Post content (markdown)
}) {
    // Create the CMA client
    const client = contentful.createClient({
        accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN
    })

    // Get your Contentful space
    const space = await client.getSpace(process.env.CONTENTFUL_SPACE_ID)

    // Get your Contentful environment
    const env = await space.getEnvironment('master')

    // Our entry to be created
    const entryFields = {
        title: {
            'en-US': title
        },
        slug: {
            'en-US': slugify(title)
        },
        user: createLinkObject(user.sys.id),
        category: createLinkObject(category.sys.id),
        content: {
            'en-US': content
        }
    }

    const contentType = 'blogPost' // Contentful model type

    // Execute entry creation
    const entry = await env.createEntry(contentType, {
        fields: entryFields
    })

    // If we get a new entry ID, then success, otherwise default to null
    const newEntryId = entry.sys.id ?? null;

    if (newEntryId) {
        // Publish our entry, if success
        const pub = await entry.publish();
        return ({
            'status': 'success',
            'entryId': newEntryId
        })
    } else {
        console.log(entry)

        return ({
            'status': 'error',
            'message': 'Failed to create post'
        })
    }
}

The first function slugify converts our title into a URL-safe string to use as our slug. You may want to add some logic to check for an existing slug, but it would be unlikely you would publish two posts with the exact same title.

In the createPost() function, we first instantiate the CMA client, then we pull our Contentful space, then our environment (most likely master). Next we create our entry object and use the createLinkObject() function to add the user and category. Finally, we execute createEntry() with the Content Type and Entry object. If we detect a new Entry ID, then all went to plan!

I hope you found this tutorial useful. Contentful is a great general-purpose backend for statically-generated pages and I keep finding new and exciting uses for it.

For more great information, please visit Our Blog.


Loading comments...