Exploring Headless CMS

Jahangir Anwari
Tarka Labs Blog
Published in
9 min readMar 11, 2021

--

Source: Rueblinger

For the while now I have heard rumblings of Jamstack and in particular the buzz around the world of headless CMS (Content Management System) so I decide to take a dive and explore what the hype is all about.

Jamstack… what’s that?

Jamstack is a software architecture based on three building blocks; client-side JavaScript, reusable API and prebuilt Markup. The underlying principle of Jamstack is to deliver your application as flat HTML as quickly as possible and achieve dynamic behaviour by calling various self-owned and third-party APIs. Headless CMS is one of the popular Jamstack approaches.

What is a Headless CMS?

Before diving in, it would be good to understand what really is a headless CMS. Here’s a definition found on Wikipedia:

A headless CMS is a back-end only content management system (CMS) built from the ground up as a content repository that makes content accessible via an API for display on any device.

The three key points in the above definition are:

  • back-end only
  • content repository
  • accessible via API

Therefore, when working with a headless CMS we are focusing primarily on the content storage and retrieval and NOT the presentation of content. In other words, a headless CMS provides you with tools to manage content and an API to pull the content.

Why use a “Headless” CMS?

Before the smartphone revolution (circa 2007) content on the internet was primarily consumed using a browser. During those times a traditional CMS would consist a whole package that would include:

  • Backend: Database, Admin interface, Plugins
  • Frontend: Templating engine, Views layer

As technology evolved over the past decade consumption of digital content expanded to multiple viewports and devices like smartphones, tablets, smartwatches, etc. Although traditional CMS use of responsive design would accommodate different viewports it would fail for devices that are consuming content outside the realm of a browser. This is where headless CMS shines as it inherently separates the data layer from the presentation layer, it provides an API to pull content and delegates the concern of presentation to the frontend application.

Minimum Viable Product (MVP)

To facilitate the exploration I set myself a goal to build the backend for news publishing website. The MVP should includes the following features:

  1. Post story articles with image
  2. For the home page provide the editor the ability to select from three layout variations for “Top Stories” section:
  • One large top story
  • Two top stories
  • Three top stories

Here’s a visual representation of the above home page layout options

The Solutions

Although the ecosystem of headless CMS has a plethora of choices available, in the interest of time I decided to look at the following three solutions:

Strapi

Strapi is one of the most popular open source headless CMS. When compared to the other two headless CMSes (Sanity and Contentful) it gives you complete control over the backend storage and admin interface used.

Setup

Below is how we would quickly setup a Strapi project along with GraphQL plugin using command line.

$ npx create-strapi-app strapi-publishing --quickstart
$ strapi install graphql

Next we need to create an admin user by accessing: http://localhost:1337/admin.

Content Modeling

Strapi has rich constructs to model entities in your system. There are two types of models:

  1. Content Types: Traditional models that represent an entities in your system like post, tags, etc..
  2. Components: Represents a custom collection of information (e.g. card component containing an image, title and summary fields).

The “Content Types” model are further categorized into two kinds:

  1. Collection Type: Allows multiple instances of the type (e.g. post).
  2. Single Type: Allows only a single instance of the type (e.g. homepage).

For our MVP we would need a “Content Type” type model that is of “Collection Type” kind called article. We will create is using CLI as follows:

$ strapi generate:api article title:string summary:string body:richtext --draft-and-publish

We would also like to add a cover image attribute to this model. But since this depends on strapi-plugin-upload it is easier to do this using the admin panel:

Next we need to model our home page. As the home page is primarily a collection of existing articles we would need to create a “Component” type model that is of “Single Type” kind called homepage. There is no CLI options provided to create this type so we can use the Content-Types Builder in the admin panel to create this type.

First we build an ArticleCard component that would store the necessary information to display a story on the home page. This component would be reused to create the different layouts of the homepage.

Next we would create three additional components for the three different layouts:

  • OneTopStory: Contains ArticleCard as single component.
  • TwoTopStories: Contains ArticleCard as repeatable component with validation rules of max and min value of 2.
  • ThreeTopStories:Contains ArticleCard as repeatable component with validation rules of max and min value of 3.

Strapi has a powerful native feature called Dynamic Zones which allows you to create complex UI structure dynamically. Using Dynamic Zones we can quickly implement the feature that would allow the editor to choose between the three different layout without having to write any code.

Querying content

With the content modelling completed for our MVP we can add content and query them in GraphQL playground at http://localhost:1337/graphql

A few additional learning:

  • The API endpoint and admin panel are the same code. This simplifies adding your own customization and business logic. The downside off course is that you are responsible for managing the API endpoint.
  • Strapi provides lifecycle hooks for models. This can be very helpful if you want to perform custom business logic before returning results.
  • Policies allows you to implement cross-cutting concerns easily.
  • Limited document workflow. Currently, it only supports “Draft” and “Publish” workflow.
  • Deleting content types is not straightforward. It required manually deleting model/api files and restarting the server.

Sanity

Sanity is another popular headless CMS. Sanity manages the storage layer and provides a custom open source query language GROQ to query for content. Sanity Studio is used to work with content.

Setup

Below is how we would quickly setup a Sanity project using command line:

$ npm install -g @sanity/cli
$ sanity init -y --create-project "Sanity Publishing Demo" \
--dataset publishing-demo \
--visibility private

Note: If you want to start from scratch for project template choose “Clean project with no predefined schemas”

Next we start Sanity Studio

$ sanity start

Sanity Studio should now be accessible at http://localhost:3333/. If you didn’t select any pre-existing template you would see the following message:

Empty schema

Your schema does not contain any document types. If it did, those types would be listed here. Read more about how to add schema types.

So let’s create our document types.

Content Modeling

Creating models in Sanity is a manual process that requires creating JavaScript files to define the models in your system. Sanity supports a number of schema types. The two important types that we will use are:

  • document: Traditional model that represent an entities in your system like post, tags, etc..
  • object: Represents a custom collection of information (e.g. card component containing an image, title and summary fields).

For our MVP we would need a document type called article. Inside schemas folder of your project create a file called article.js with following lines of code:

Next we need to tell Sanity about our new document type by adding it to schemas/schema.js

Sanity Studio reloads and now you should see the Article document type listed under “Content”.

Next we will replicate the steps we followed in Strapi by creating the necessary object types as follows:

  1. articleCard: contains headline , summary and cover attributes along with a reference to article
  2. oneTopStory: contains articleCard as single field.
  3. topTwoStories: contains two articleCard as fields grouped in fieldset.
  4. topThreeStories: contains three articleCard as fields grouped in fieldset.
  5. topStories: applies page builder pattern that allows editor to select one of the three layouts.

With the required object types in place we can now create a homepagedocument type that has a single topStories field.

After creating the required types our schema.js looks like this:

Once Sanity reloads we can see our HomePage type available in Sanity Studio:

Querying content

With the content modelling completed for our MVP we can add content and query them in GraphQL playground. But before we can query in GraphQL we need to deploy

$ sanity login
$ sanity graphql deploy

Once the deployment completes the GraphQL playground is accessible at https://[projectId].api.sanity.io/v1/graphql/[dataset]/default

A few additional learning:

  • To retrieve content Sanity provides multiple CDN backed endpoints (i.e. API CDN, Asset CDN). This makes your backend robust. The downside is that your ability to perform customization on the backend is limited.
  • As Sanity Studio is a React app it can be easily extended.
  • Sanity Studio can be customized for complex layout Structure Builder.
  • You can implement a custom document workflow.
  • Custom access control is available in enterprise plan.
  • Localization can be done at field or document level. But it requires some effort in getting it setup.

Contentful

Contentful is another well known headless CMS. Unlike Sanity and Strapi the admin interface is hosted by Contentful.

Setup

To create a Contentful application we first need to create a space. This guide provides a good explanation of setting up a space.

Content Modeling

Contentful support creating custom types and provides a number of field types. The CLI tool currently doesn’t support creating content types from command line. So we use the content management web app to create the models. Under “Content model” we create Article type with following fields:

For home page content type tried to create reusable component like ArticleCardby following this blog post but could not get it working. So ended up with a working but not ideal solution of creating a 1-many reference to articles and a checkbox to select a layout. This solution has no validations and relies on editor making the correct selections.

Querying content

With the content modelling completed for our MVP we can add content and query them in GraphQL playground. To do that we would need to install the app first by going to https://app.contentful.com/spaces/[space-id]/apps?app=graphql-playground

After installation you can access the GraphQL playground at this URL: https://app.contentful.com/spaces/[space-id]/apps/app_installations/graphql-playground/graphql

A few additional learning:

  • Existing apps are available for installation from Contentful’s marketplace.
  • Has rich validation and appearance options for fields.
  • Has localization support out-of-the-box.
  • Adding custom behaviour to the admin interface requires developing a custom extension by following the painful approach of creating a contentful app and then installing it to your space.
  • Similar to Sanity, Contentful provides multiple CDN backed APIs (e.g. Content Delivery API, Images API, etc.)
  • Running a few nested GraphQL queries to retrieve embedded entries information would too often result in TOO_COMPLEX_QUERY error. Could be because I was using a “free” space.

Conclusion

Headless CMS provides a quick and flexible way of managing and pulling content. However, choosing between the different solutions available depends on specific requirements of the project. Below are a few requirements and suggestion on which would be the suitable CMSes.

  • Full control: Strapi
  • Execute custom business logic when handling API query: Strapi
  • Ease of customization content management interface: Strapi / Sanity
  • Ease of creating custom types: Strapi / Contentful
  • Out-of-the-box CDN support for API endpoints: Sanity / Contentful
  • Localization: Contentful
  • Marketplace: Contentful / Sanity

Hello Montreal! We’ll be at the Startupfest ’22 all three days, so drop by and say hi. We’d love to see what you’re building, and early birds get a free (limited) design audit or tech consultation. DM us on Twitter (@tarkalabs) and let’s talk.

--

--