ButterCMS: A Headless CMS

By Brandon Nicoll, Software Engineer, ButterCMS

This tutorial explores creating and managing content for an example online store using ButterCMS, a fully managed headless content management system (CMS). You learn how to deploy your store app by using App Engine and examine some options for scaling and monitoring the app.

Web developers are often asked to build websites where other teams manage the content that is published. On the Earth Class Mail website, for example, web developers create and maintain the homepage, but the marketing team administers most of the content.

Traditionally, you might manage such content by using CMS templates. With respect to site development, design, and maintenance, your challenge is how to integrate the CMS-managed sections into the rest of the website. Now you have two systems to maintain: the CMS and the web framework you used to develop the main website. Moreover, in such systems, you typically maintain a separate CMS admin site and database for the content editors to log in and manage content.

A headless CMS solves these issues, by allowing you to feed CMS content into your existing website, in your existing framework, and leveraging existing style assets. The primary difference from a traditional CMS is how the headless CMS retrieves and consumes content and data. With a traditional CMS, you rely on templates and plugins to assemble web pages. With a headless CMS, you retrieve relevant content by using simple API calls, and integrate that content with a web framework to build rich, and fully customizable web pages. This difference decouples the content from the website, allowing content editors to make updates without needing to change templates and redeploy the site.

ButterCMS

ButterCMS, or simply "Butter," is a software-as-a-service (SaaS) offering for headless CMS. Butter minimizes or eliminates the setup, configuration, and hosting of the CMS backend. Butter offers client libraries for most of the popular programming languages and frameworks used for web development. Using these client libraries, you can integrate managed content into your app. And if Butter doesn't support your preferred language or framework, it offers a REST API. In addition, Butter also provides a user interface (UI) for content editors.

App Engine

In this tutorial, you use App Engine to deploy your store app. App Engine is a fully-managed developer experience to deploy apps built by using Node.js and many other web frameworks. App Engine automatically scales the service in response to load and takes care of such operational concerns as provisioning, health checking, load balancing, and healing. App Engine lets you focus on writing code to create business value rather than on managing infrastructure.

Objectives

  • Use Node.js and ButterCMS to develop a simple backend service, or content service, that powers the website for an example store.
  • Develop a simple frontend for the website using Node.js and the content service.
  • Use App Engine to deploy the content service and the website.
  • Use App Engine to set up scaling for the website.
  • Use Cloud Monitoring to check the health of your content service and website.

Costs

This tutorial uses App Engine, which is a billable service. You can use the pricing calculator to estimate the costs for your projected usage. New Google Cloud users might be eligible for a free trial.

Before you begin

  1. In the Cloud Console, on the project selector page, select or create a Cloud project.

    Go to the project selector page

  2. Make sure that billing is enabled for your Google Cloud project. Learn how to confirm billing is enabled for your project.

  3. Install and initialize the latest version of the Cloud SDK:

    Download the Cloud SDK
  4. Create a ButterCMS account.
  5. Prepare your environment for Node.js development.

    Go to the Setup Guide

Architecture overview

You use Butter to define and manage the content for the store website, and you use the Butter API to retrieve that content. A content service encapsulates the implementation details for communicating with the CMS to retrieve the content. The content service converts the content into a format that the website's presentation logic can consume. This logic communicates only with the content service and not with Butter. This decoupling allows you to keep the system extensible and maintainable. For example, you could combine content from different sources or swap Butter with another headless CMS simply by changing the content service and keeping the rest of the system unchanged.

Butter architecture.

Managing content in Butter

To manage the dynamic content for the store website, you use a feature of Butter called collections. Collections are lists of user-defined objects, where each object encapsulates information about an individual entity. In this example, an object represents a product listed on the store website. Butter handles storing all the information and provides a UI where you can manage that information.

Create a workspace

Before you can create a collection, you first need to create a workspace. A workspace provides a framework for organizing the content and might represent individual pages or groups of related content.

On the navigation menu in the Butter admin page, click New Workspace.

New Workspace.

Create a collection

Now that you have a workspace, you can create a collection.

  1. In the workspace you just created, click Add Content Field, and then click + New Collection.

    New Collection.

  2. Configure your collection.

    product properties.

  3. Click Create Collection, enter a name for the collection, and then click Save as a Collection.

    Collection Name.

  4. Click + Add to collection, where collection is the name you specified in the preceding step.

    Add to collection.

  5. Fill in the content fields and click Save Item.

    Save Item.

    On the collection overview page, you see the newly added content for your collection:

    Newly added content.

Edit content

After you've defined a collection and populated it with your initial data, the site's content editors can follow the same steps to change existing objects, add new objects, or remove objects that are no longer needed.

content editing.

Creating a content service

This section assumes that you have installed Node.js and npm on your workstation. Unless instructed otherwise, you enter the following commands in a command shell on your workstation.

  1. Create a directory for your content service project:

    mkdir contentService
    cd contentService
  2. To initialize the project for Node.js, enter npm init.

  3. Follow the prompts to enter any relevant information.

  4. Install the npm packages buttercms and express. express is a web application framework, and buttercms is a client library for Butter's API.

    npm install express --save
    npm install buttercms --save

Implement the service

To implement the content service, you need your Butter API key.

  1. To find your key, go to Settings.

  2. In your text editor of choice, create a file server.js that will contain the code for the content service.

  3. In server.js enter the following, replacing [YOUR KEY] with your Butter API key:

    const express = require('express');
    const app = express();
    const butter = require('buttercms')(‘[YOUR KEY]');
    
    app.get('/hotproducts', function (req, res) {
      butter.content.retrieve(['hotproducts']).then(function(resp) {
        var hotProducts = resp.data.data;
        res.json(hotProducts);
      });
    })
    
    const PORT = process.env.PORT || 8080;
    app.listen(PORT, () => {
      console.log(`App listening on port ${PORT}`);
      console.log('Press Ctrl+C to quit.');
    });
    
  4. Start the server:

    node server.js
    
  5. Go to http://localhost:8080/hotproducts. You see the following JSON representation of the products you added earlier:

    {
       "hotproducts":[
          {
             "url":"https://store.example.com/tshirt",
             "description":"A t-shirt with Brandon on it",
             "productname":"Brandon T-Shirt",
             "previewimage":"https://cdn.buttercms.com/image1",
             "priceinusd":14.99
          },
          {
             "url":"https://store.example.com/mug",
             "description":"A mug with Brandon on it",
             "productname":"Brandon Coffee Mug",
             "previewimage":"https://cdn.buttercms.com/image2",
             "priceinusd":6.99
          }
       ]
    }
    

    You are now ready to deploy the service to App Engine.

Deploy the service

This section assumes that you have Cloud SDK installed and initialized and that the project directory you created earlier (contentService) is the current working directory.

  1. Create a file named app.yaml that contains the following:

    runtime: nodejs
    env: flex
    
  2. Deploy the app:

    gcloud app deploy
    

    As the code is built and deployed, progress updates are displayed in the terminal window. The output might look familiar if you have used Docker to build container images before. When the deployment is done, the content service is live and publicly accessible.

Implementing a frontend website

At this point, the content service is ready to be consumed by a frontend. In this section, you create a frontend, a crude implementation of a website, to pull in the content data from the content service and return HTML.

Create the website

  1. Create a directory for the frontend:

    mkdir eCommerceSite
    cd eCommerceSite
  2. Initialize the project for Node.js:

    npm init
    
  3. Follow the prompts to enter any relevant information.

  4. Install the express npm package:

    npm install express --save
    
  5. Install the npm package request, which you use to make the REST call to contentService:

    npm install request --save
    
  6. When you deployed contentService to App Engine, you were given a public URL. Make note of that URL, open your text editor, and create a server.jsfile that contains the code to return the homepage HTML.

    const express = require('express');
    const app = express();
    const https = require('https');
    
    const options = {
      hostname: 'contentservice-173519.appspot.com',
      port: 443,
      path: '/hotproducts',
      method: 'GET'
    }
    
    app.get('/', function (req, res) {
      var html = '';
      const contentReq = https.request(options, (contentRes) => {
        contentRes.on('data', (d) => {
          var data = JSON.parse(d);
          for (i = 0; i < data.hotproducts.length; i++) {
          html += '<li>' + data.hotproducts[i].productname + '</li>';
        }
        res.set('Content-Type', 'text/html');
        res.end(html);
        });
      });
      contentReq.end();
    });
    
    const PORT = process.env.PORT || 8080;
    app.listen(PORT, () => {
      console.log(`App listening on port ${PORT}`);
      console.log('Press Ctrl+C to quit.');
    });
    

Deploy the website

Follow the same instructions as in the previous section to create an app.yaml file, and then run gcloud app deploy to deploy the website to App Engine.

Scaling

App Engine provides several scaling options that you can use to configure how the content service and the example website scales in response to load. You specify these options in app.yaml.

Manual scaling

With manual scaling, you set the exact number of App Engine instances you want the app to run in. If you later change the number of instances, you must update app.yaml manually.

The following is an example app.yaml entry for manual scaling:

manual_scaling:
  instances: 2

Basic scaling

With basic scaling, you set the maximum number of instances and the amount of time an instance can go without serving a request before being shut down. This option enables App Engine to shut down unneeded instances.

The following is an example app.yaml entry for basic scaling:

basic_scaling:
  max_instances: 3
  idle_timeout: 3m

Automatic scaling

Automatic scaling offers a wider range of parameters that define how App Engine manages scaling for the app, for example, setting minimum and maximum instances.

The following is an example app.yaml entry for automatic scaling:

automatic_scaling:
  min_num_instances: 5
  max_num_instances: 10
  min_pending_latency: 45ms
  max_pending_latency: automatic
  max_concurrent_requests: 100

You can find more details about these scaling options for Node.js applications.

Monitoring

With your content service and website up and running, you can monitor their health by using Cloud Monitoring. This section describes how to create example email alerts for 95-percentile response time latency exceeding a threshold.

Add a Cloud Monitoring alerting policy

To create an alerting policy, do the following:

  1. In the Google Cloud Console, go to Monitoring or use the following button:
    Go to Monitoring
  2. In the Monitoring navigation pane, select Alerting and then select Create Policy.
  3. Enter a name for the alerting policy.
  4. Click Add Condition:
    1. Complete the Target and Configuration sections.
    2. Click Add.
  5. (Optional) Repeat the previous step to monitor another resource.
  6. (Optional) Click Add Notification Channel and enter your notification channel information.
  7. (Optional) Click Documentation and add any information that you want included in a notification message.
  8. Click Save.

Cleaning up

To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial:

  1. In the Cloud Console, go to the Manage resources page.

    Go to the Manage resources page

  2. In the project list, select the project that you want to delete and then click Delete .
  3. In the dialog, type the project ID and then click Shut down to delete the project.

What's next

  • Try out other Google Cloud features for yourself. Have a look at our tutorials.