september 24, 2023

{ adding a blog to a react site with a headless cms from strapi, part 1 }


tags: headless cms, strapi, react

I was able to set up a headless CMS to manage a blog on my personal site. For a while, I've been interested in headless CMS solutions, and the Jamstack in general.

There are many, many options for a headless CMS. I definitely spent more time researching which one to use than I did actually installing the one I chose, which was Strapi in this case since I could use it on the free tier so long as I hosted it myself (which will be its own blog post) and it has a very simple REST API. I wanted speed here rather than learning...although I still learned plenty!

What is a Headless CMS?

Prior to software engineering, I did web design and content writing with a company that relied almost exclusively on WordPress. WordPress has been the leading CMS for almost 2 decades and an amazing amount of sites are built on it. The difference between WordPress and a headless CMS is that WordPress contains your database, your front end design, and your content.

A headless CMS pulls your content and the database where your content is stored away from your front end. The content is then delivered via API to the front end. This opens up the world of Jamstack, which relies on a static site generator to build a static front end and APIs to deliver the dynamic content. (The JAM initially stood for JavaScript, APIs, and Markup.)

This appeals over WordPress to me for several reasons. I dealt with so many WordPress sites that relied on terribly coded themes and buggy plugins. Security vulnerabilities are rampant. And I don't code in PHP, which is what WordPress runs on.

Mind you, it is still possible to use WordPress as your headless CMS and just use the content management functionality, but I heard the API wasn't great. Also, this wasn't what WordPress was designed for, so it makes more sense to me to use a dedicated headless CMS.

I looked into several and many seem great. I'm using Sanity for another project and decided to give Strapi a go for this blog.

Setting Up Strapi

First off, Strapi has excellent documentation, both on the user side and the developer side. Props to them for that. Their interface is generally attractive and mostly intuitive to use. Some things I'm still unsure about and learning, but for this small project, it's been perfect.

I mostly just followed the Quick Start, which gave me a blog in my development environment in less than an hour.

The one difference was that I had to use the custom CLI installation since I was using Postgres from the start (I think their default is SQLite).

Getting started looked like this:

npx create-strapi-app@latest my-project

I set my custom options to:

  • JavaScript
  • postgres
  • 127.0.0
  • 5432
  • No SSL (this is an important one for Postgres; it doesn't use an SSL connection by default)

The username and password for the database were a struggle because when are they not? I guessed during set up (apparently the postgres defaults are "postgres" and "password") and then went into my .env in my new Strapi project to fix them since I had guessed wrong.

My username was my name, which was good choice, past-Juliane. When you go into psql in your terminal, whatever the default db is (the prompt it's giving you) is your username. My password is lost to history because of encryption. So I updated my password with:

ALTER USER juliane WITH PASSWORD 'newpassword';

When everything is correct, npm run develop will take you to the Strapi admin panel for your project at http://localhost:1337/admin.

The Strapi Admin Panel

I started at the Content-Type Builder plugin. There are a few options here:

  • Collection Types - manage several entries, like blog posts (just what I need!), user profiles, etc.
  • Single Types - manage only one entry, like a homepage (my homepage is hard coded so that's not a concern...At least, today I am not letting it concern me.)
  • Components - building block pieces that can be used in collections and single types.

Strapi Content-Type Manager adding a rich text block called content

You build a new type with the different pieces it should have (like text, rich text, email, password, etc). Neat. All I need is some rich text, but if I decide to make this blog fancy some day, I'll come back and check out the extensions I can add. There was a cool one for adding content tags, but that's for later.

Strapi Content-Type Manager dashboard with a collection type called personal blog with one content component

Then in the Content Manager, I can create a new entry for my personal blog.

API for Strapi in the Front End

First, we have to alter the permissions. This is through Settings in the admin panel. I went to Roles then my Personal-Blog content type. I checked find and findOne.

Then with content published, a quick test in Insomnia to make sure I can get all my posts.

insomnia fail.png

That would be a no.

api id.png

Okay, apparently Strapi adds an "s" to the API ID. According to their docs, the endpoint is /api/:pluralApiId.

How thoughtful of them. But anyway, the request works. Let's add this to the site!

Insomnia GET request to Strapi to the endpoint personal-blogs with a successful response

I have a nice function to call my API and clean up my data. I also added a sort parameter, which is "?sort=publishedAt:desc" in my case so I can show the most recent blog first.

    static async getPosts() {
        const resp = await axios.get(
            `${STRAPI_BASE_URL}personal-blogs${STRAPI_SORT_PARAM}`
        );
        const posts = resp.data.data.map(post => {
            const data = {
                id: post.id,
                content: post.attributes.Content,
                date: post.attributes.publishedAt,
            };
            return data;
        })

        return posts;
    }

Story time: the first API I ever worked with sent the data back under a key of data. To access it, you had to use resp.data.data. This was one of the sticking points we were meant to stumble on in class. But it was so scarring that, to this day, I still type resp.data.data at first when I am calling an API. Even my own APIs. So thank you, Strapi, for finally making that work on the first try.

I've added a Blog and a BlogPost component, but I'm suddenly realizing I want routes for the BlogPosts. And that means I need a permalink that mirrors the title because an ID is not going to be very SEO friendly.

Back to Strapi. Title and permalink added. API call updated. Components updated.

Now one last fix. Strapi provides the content in markdown. But of course there's a library for that. I installed React Markdown and applied it to my blog post.

Added some styling and now we're ready to move it to production.

I think I do want to add some sort of category or tagging system. Strapi does have built in relationships or there is a tagging extension. I will have to explore and see which will work best - and hopefully without too much refactoring.

But in the meantime, stay tuned for the deployment.