An Introduction to API-First CMS with Directus' Open Source, Headless CMS

In a rush? Skip to tutorial steps or GitHub repo & live demo.

"Off with their heads!"

The frontend developers' call to arms echoed throughout the realm. All across the Internet lands, monolithic, traditional CMS shivered.

Seriously though, we're finally going to discuss API-first CMS—aka decoupled/headless CMS—on the blog.

From GitHub forks to email inquiries, we've noticed an increasing interest in "going headless" in general, but also for e-commerce purpose. So today, we're going to:

  1. Discuss the what, why and when of API-first CMS.
  2. Show how to code a transactional web app with content managed in Directus, an open source headless CMS.

More specifically, I'll explain how to build a lookbook using a full JAMstack: Metalsmith, Vue.js, Snipcart, and Directus. I'll even throw in an open source code repo & live demo. :)

First, let's try to understand how API-first CMS can add value to your workflow.

What is an API-first, headless CMS? Why use one?

Like traditional content management systems, API-first CMS let users manage content through a web UI. So how do they differ?

API-first CMS allow developers to decouple content management from content rendering. A coupled CMS, like WordPress, takes care of both: content is stored in a backend database AND rendered in frontend templates using HTML/CSS. So the "head" that's missing from a headless CMS is actually that final "presentation layer":

Unlike a traditional CMS, an API-first CMS exposes its content data via a consumable API.

Your headless CMS isn't concerned about how you choose to display content. It pushes raw content (e.g. JSON or XML) for you to fetch and display anywhere: mobile app, static site, web app, desktop app, IoT device... or all of these at once!

api-first-cms-headless-architecture

Headless CMS architecture

So why have they become popular? Why are companies like the NY Times, Lenovo, Spotify, Nike, Apple, Microsoft & New Relic using them?

Because the web has evolved! Frontend tooling & frameworks have exploded. Traditional CMS have become limited in how they display content and are prone to many security exploits. Cross-platform content management has become essential to many projects. Static site generators have resurfaced, opening a content management gap API-first CMS could fill, saving non-technical folks from editing Markdown files.

We have many tools and channels to build digital experiences today. With headless CMS, content can seamlessly follow different forms, not be limited by one.

API-first CMS: Benefits

  • Organizations save time & money: reduction of overhead for cross-platform content management.
  • Developers get more freedom: ditching CMS templates makes for authentic UX. Fewer worries about backend scalability and maintenance.
  • Content is future-proof: reduced impact of migrations & re-designs since content is decoupled from frontend.
  • In some cases, more performance: content is pre-baked and ready to be served.

API-first CMS make for a clear separation of concerns which enhances developer productivity. They foster a technology-agnostic approach to development that resonates with our own product's values.

Drawbacks

Most potential drawbacks (supporting user permissions, multi-languages, etc.) have already been solved. Still, a few potential issues worth mentioning:

  • Might be overkill for a simple website project.
  • Might not provide responsive UI for on-the-go content management.
  • Might not support website tree architecture for content navigation.
  • Might be expensive for client budget.

List of headless CMS

Before moving on to the tutorial, here's a non-exhaustive list of full-fledged headless CMS developers should check out:

You can also find a repository of static & headless CMS here, and add to the list with a fork.

Note: We know some clients might insist on using traditional CMS vendors like WordPress or Drupal, for administrative reasons. In that case, know that API-first CMS approaches are still possible: use your coupled CMS as a backend UI to store content, and add a JSON/REST API on top. This'll make your frontend independent of, say, your Drupal install (we'll likely discuss a decoupled Drupal set up soon here). Also, you might want to check out Pantheon's resources on decoupling traditional CMS.

Directus: an open source, headless CMS

directus-tutorial-open-source-headless-cms

We're about to step onto more practical grounds!

For this tutorial, we're using Directus, a free, open source headless CMS. Let's first hear about it from Ben Haynes, Project Lead at Directus:

Directus is a "headless" CMS & API that decouples your content so it can be connected anywhere and everywhere. With a highly extensible DBaaS core elegantly wrapped with an intuitive admin web-application, Directus can mirror even the most complex database architectures. Best of all, this premium framework is completely free and open-source.

When asked what set Directus apart, Ben mentioned a unique combination of 3 key things:

  • Headless CMS & comprehensive API
  • Complete control over database schema
  • Free & open source

Note: paid hosted instances are also available.

We picked Directus because 1) we had never played with it + it clearly fit our content modelling needs, 2) we received a warm response from the founders on their Slack community and 3) we loved their bunny branding.

Lookbook tutorial with Directus, Metalsmith, and Snipcart

This section will show you how to build a "lookbook" e-commerce web app using:

  1. Metalsmith & Vue.js to generate a "static" site
  2. Directus to manage content—our products
  3. Snipcart to add buy buttons and a checkout
  4. Netlify to deploy our web app

Pre-requisites

1. Creating our products content fields in Directus

Done with the Directus installation? Good.

We're going to start this tutorial by creating our products directly within Directus' admin.

Let's generate our first "products" table:

directus-cms-demo-products-table

Time to add products.

For this demo, we'll include a women & men section in our lookbook. To do so, we'll use the category field we created. See our table data:

directus-cms-demo-products-data

2. Binding our headless CMS data to our Metalsmith build process

Our goal here? Use product data from Directus' content API to populate local views (templates, layouts, etc.) and create static assets. Keep in mind that headless CMS aren't strictly bound to this workflow: they aren't opinionated on how you use the data. They only provide a set of tools to deliver it.

Once our build process finishes, we need a folder that can be served as is for users to consume.

We decided to use Metalsmith for the "site" part of this demo. It's got a neat barebones approach, an its modularity fits our use case perfectly. We won't be explaining its inner mechanics for this short tutorial; you can refer to our GitHub repo to see the raw setup.

While writing this, we realized there was no plugin for Metalsmith to interact with Directus during the build. To achieve our humble goal, we built one (open source repo here).The plugin is simple: it fetches data from Directus and creates a file scoped to the build process for each table row (these files aren't in src folder nor in the build one).

From there, Directus data is exposed to our templates, and we can use it to generate our static assets.

Our simple Metalsmith config:

{
    "source": "src",
    "destination": "build",
    "plugins": {
        "metalsmith-markdown": true,
        "metalsmith-directus": {
          "accessToken": "{accessToken}",
          "baseURL": "https://{your-directus-instance}.directus.io/api"
        },
        "metalsmith-templates": {
          "engine": "handlebars",
          "directory": "templates"
        },
        "metalsmith-layouts": {
            "engine": "handlebars"
        }
    }
}

3. Generating the views to render our content

We now have product data and the architecture to fetch it. But we need views to render all this!

So we've created a layout view, a view for each category, and a template for products. These views are pretty basic and are using Handlebars to render their data. See this repo folder for details.

Important notice: we also need Snipcart's required scripts in the views containing our buy buttons to make this work.

We decided to create a small Vue.js component to handle switching categories and update the corresponding images in our lookbook.

It feels a little bit contrived for this example, but we thought it would be nice to show how easily you can use Vue just as a component for a part of your app.

Our little component:

var app = new Vue({
  el: '#app',
  data: {
    category: 'men'
  },
  created: function(){
    this.scrollToSection();
  },
  methods: {
    shop: function(){
      window.location.href = '/' + this.category + '-products.html';
    },
    isMen: function(){
      return this.category == 'men';
    },
    isWomen: function(){
      return !this.isMen();
    },
    updateCategory: function(value){
      this.category = value;
      this.scrollToSection();
    },
    scrollToSection: function(){
        $.scrollify({
            section : ".section",
        });
    }
  }
});

This is only bundling the small part of logic we have in our app. It handles the category state, and our views have the necessary conditionals to render accordingly.

4. Deploying our web app on Netlify

We'll now streamline our deployment process with Netlify—they offer a kickass free plan for open source projects.

On their site, we can create a project, and link it through Git:

directus-tutorial-demo-deployment1

Pushing to our repo will now notify Netlify and rebuild/publish the website automatically.

The only problem to fix now is our relative paths, such as the URL we need to declare in our products. We need to find a way for Netlify to inject an environment variable representing its domain for us to use in our views. Let's click on the add button of their "Build environment" section and add a DOMAIN key with the according value:

directus-tutorial-demo-deployment2

Now, we need Metalsmith to expose it in our views at build time. There's already a plugin for that called metalsmith-env. We'll install it and add the following part to our plugins object:

metalsmith-env": {
    "variables": {
        "DOMAIN": "{YOUR_DEFAULT_LOCAL_DOMAIN}"
    }
}

Now, when the build happens on Netlify, the variable DOMAIN will be overridden, and our relative paths will still be valid.

GitHub repo & live demo

api-first-cms-web-app-demo

Have a closer look at the live demo and code for this integration!

See GitHub code repo

See live Snipcart + Metalsmith + Directus demo

See Metalsmith-Directus plugin repo

Closing words on Directus & API-first CMS

Recently, we've seen more developers craft interactive e-commerce experiences with our product (like this one). Choosing a headless approach can be a smart for such projects, so I hope this lookbook demo inspires others to do similar stuff!

I encourage you to give Directus a try if you're looking to build something with an open source headless CMS. Integrating it was super straightforward, and the product offers an A+ UI/UX. The Directus API documentation was also top notch.

This whole integration took me more time than expected—about 8 hours. That's mainly because I got caught up in the fun parts (building a Metalsmith plugin, giving more love to the live demo). We could have pushed a few things further too! Solidifying the plugin, for one. Creating more content types in Directus like product variants, for instance, would have been a nice touch.

I really like the universe of possibilities that "going headless" opens. But while a content management API can often make sense, it's important to mention to not mindlessly default to that. There are still coupled modern CMS options perfect for simple websites. Like always, picking your tech stack is a complex process! ;)

Using Snipcart with other API-first CMS


Found this post valuable? Take a second to share it on Twitter. Think we missed a few things? Got thoughts on the whole API-first, headless CMS thing? Drop us a line below!

Suggested posts: