Skip to main content

Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you've safely connected to the .gov website. Share sensitive information only on official, secure websites.

Share your experience with the FAS IT-Playbook by taking this brief survey

Cloud Plays - Headless Drupal

Context

Sometimes the built-in UI abilities of Drupal will not adequately meet the requirements of the desired user interface. In such a case, can Drupal still be used as a content management engine (back-end) for a web application while using another technology for the user interface?

Problem

How can I use the Drupal engine to deliver content to another user interface framework like Angular or React?

Examples

Three examples come immediately to mind:

1. Multiple consumers of content

Since most communication happens not only through the websites but through various other multiple channels like mobile apps. CMS, therefore, is not used only to send content to web browsers but to push content to various other places.

2. Microsite Management

Sometimes we need to create multiple websites which are separate (eg. 8aStars, VSC, VASales), but which will share a lot of content. In such a case, it might be easier to create one content engine (e.g. using headless Drupal) which will deliver content to all the microsites. The microsites can be quickly created and closed whenever a need arises and the content can be contained in one content hub.

3. An elegant UI is needed

Drupal is fantastic for content creation, data storage etc, but it is written in PHP which is a server-side rendering engine. If the particular website or app or website requires a very elaborate UI or simply is interactive it probably has to be built in javascript.

Javascript allows us to create fantastic interactions which are easy to use and fast. Libraries like Angular, React or Vue help developers quickly create complicated frontend applications. Progressive Web Applications - a standard published by Google is also gaining momentum and it requires an application to be built in javascript.

Solution

A single solution for all of these is to access the content management system through an API layer.

Structure

If content is delivered through RESTful API, it could be rendered by any front end channel. Therefore, a mobile browser, mobile app, kiosk display, IoT, or an internal module of a large website could use the same content endpoints. This is a force multiplier since content can be easily shared across multiple websites through one content engine. Currently, any solid user interface technology could use the API, providing perhaps many elegant UIs on phones, tablets, web sites, etc.

In this case, the content management system is Drupal since it has been vetted and is well established in the FAS cloud ecosystem. It has a solid RESTful API and is available in both EBTA and MCaaS platforms. In a decoupled Drupal architecture, instead of the Drupal’s theme layer, a client-side framework (e.g. AngularJS) is used. This client-server pattern is a well worn and understood pattern where the UI framework is responsible for making the connection to the Drupal server and then rendering the HTTP/JSON responses into suitable UI components.

Headless Drupal

Implementation

Required Cloud components:

Headless Drupal Technical Architecture (e.g. Vendor Support Center)
Dataflow (e.g. Vendor Support Center)

Drupal Steps:

Install the following modules essential for creating Drupal views to export the content in JSON format - Rest Views module https://www.drupal.org/project/rest_views - This module will help in serializing and better output for Rest API calls - Created views in Drupal to export the nodes in JSON format. - Search API module https://www.drupal.org/project/search_api - Created a Drupal view for Search export in JSON format. - API Modules https://www.drupal.org/project/JSONapi_resources - https://www.drupal.org/project/JSONapi_search_api - These 2 modules are installed in Drupal for retrieving JSON export of the Drupal content. We installed these modules specifically to get the content which is not published or in draft mode. - Webforms for tables https://ftp.drupal.org/files/projects/webform-6.0.3.tar.gz - Webform views https://www.drupal.org/project/webform_views - The webform modules are installed to create editable tables in Drupal and export the content in JSON format through Drupal views - miniOrange module for integrating secure auth single sign on - https://plugins.miniorange.com/drupal-single-sign-sso-using-miniorange-idp

Angular Steps:

Use **@angular/common/http** module to make HTTP client calls to the Drupal views and retrieve the content in JSON format. The JSON content is beautified and loaded through content viewers in Angular. #### Code samples: To provide a deeper understanding, the following code snippets might be useful for understanding the connection to the Drupal server for a Javascript UI framework.
import { HttpClient } from '@angular/common/http';



 getTopics(nid) : Observable<any>{

    let topicUrl = this.serverUrl + this.viewnodeapi + 'topic-view/' + 

nid + '?' + this.params;

    return this.http.get(topicUrl)

  }
The drupal content node can be exported as JSON content using **?_format=JSON** as a query parameter For eg. https://vsc.gsa.gov/drupal/node/4 node can be read through JSON format by appending ?_format=JSON and it will look like https://vsc.gsa.gov/drupal/node/4?_format=JSON The topicUrl in the above code example transforms to something like https://vsc.gsa.gov/drupal/node/4?_format=JSON We will loop through the JSON content and format the required data accordingly.
   this.drupal.getTopics(this.sectionNode).subscribe(resp => {

        this.templateData = resp;

        if (this.templateData[0].field_subtopics_export) {

          for (let i = 0; 

i < this.templateData[0].field_subtopics_export.length; 

i++) 

    {

            // gets title to compare for scrollTarget match

            let title = 

this.templateData[0].field_subtopics_export[i].title;

            if (title == this.scrollTarget)

{

              console.log('Found a match for "' + title +'"');

              this.loading = false;

              this.scrollToTarget();

            }

            if (this.templateData[0].field_subtopics_export[i]

.custom_articles) 

  {

              this.drupal

.getArticles(this.templateData[0]

.field_subtopics_export[i].id).subscribe(resp => {

                this.templateData[0].field_subtopics_export[i]

.custom_articles_1 = [];

                let customArticleOrder = [];

                for (let k = 0; 

k < resp.field_custom_articles.length; 

k++)

 {

                  let id = resp.field_custom_articles[k].target_id;

                  customArticleOrder.push({nid: id});

                  // subscribes to populate data

                  this.drupal.getArticles(id).subscribe(resp => {

                    // prepares article info for list insertion

                    let articleTitle = resp.title[0].value;

                    let articleBody = resp.body[0].value;

                    // checks for and removes end whitespace if present

                    if (articleTitle[articleTitle.length -1] == ' ') 

  {

                      console.log('Removing whitespace: "' 

+ articleTitle + '"');

                      articleTitle = articleTitle.substring(0, 

articleTitle.length - 1);

                    }

                    let article = { nid: id, 

title: articleTitle, 

body: articleBody, 

subArticles: resp.field_additional_bodies};

                    // gets position in order array for nid

                    let index = customArticleOrder

.findIndex(x => x.nid === id);

                    // inserts article at index position

                    this.templateData[0].field_subtopics_export[i]

.custom_articles_1[index] = article;

                    if (articleTitle == this.scrollTarget) {

                      console.log('Found a 4th level match for "' + 

 articleTitle +'"');

                      this.viewportScroller

.scrollToAnchor(this.scrollTarget);

                      (async() => {

                        await new Promise (f => setTimeout(f,100));

                        this.viewportScroller

.scrollToAnchor(this.scrollTarget);

                        this.loading = false;

                      })();

                    }

                  });

                }

              })

            }

          }

        }

        this.jumplink();

      });

Example Resolved

All three of the examples given at the beginning are solved using a “headless” content management system like Drupal. The Vendor Support Center (VSC) specific results are given below. - Multiple consumers of content - Currently vsc.gsa.gov can cater to the mobile browser users. This can also cater to mobile apps in future. - Microsite Management - Currently the VSC drupal content engine has a lot of content which can be shared across all the eTools sites. - Need for an elegant UI - The new VSC app is an elegant single page application which loads the content seamlessly. ### Known Uses https://vsc.gsa.gov- Built on angular with headless drupal https://cic.gsa.gov - Built on reactjs with headless drupal ### Consequences #### Benefits: - Content Sharing - Content can be easily shared across multiple sites and platforms as every piece of content is exposed as rest API - UI flexibility - If we move away from the traditional drupal ui, we have more flexibility in rendering the content - Diversification of teams - Different teams for frontend and backend will provide different flavors of best practices and they bring best practices of both world to the team - Microsite management - In a multiple microsite environment, headless drupal makes the setting up and tearing down of on demand microsites very easy. In the traditional drupal you can have only a single site - Reducing technological dependency on one platform - The bigger the system we build on drupal, the greater the dependency on it. Using Drupal as the only content engine and abstracting the Drupal UI allows us to be more dynamic in changing the front end technologies #### Liabilities: - Development is more complicated and more expensive - A Headless infrastructure is more complex than a traditional Drupal website. This can cause additional difficulties and can increase costs. Deciding on a headless, you should consider whether the benefits outweigh the costs - Managing multiple teams - In a headless Drupal, there are 2 separate components (frontend and backend) and these have to be developed (at least to some extent) in coordination. Quite often, there will be separate developers or teams (backend and frontend) working on the website - Sometimes these are 2 different companies. This requires coordination and much more communication because data models have to be agreed, endpoints created and tested - It is true that in Drupal many of these might be available out of the box, but most probably there will be requirements for some custom ones - Higher overall development costs - Overall development time will probably be higher since we now build 2 systems, and therefore development cost will also be higher than that of a comparable standard Drupal website, especially taking into account the coordination effort - Higher subsequent maintenance costs - Maintenance of a decoupled system is more difficult. Tests are more important when you rely on REST APIs because changes to one system might make it not compatible with the other - Deployments might have to be more orchestrated - Changes to multiple both systems are deployed all at once. Alternatively, multiple versions of an API might be maintained, but that also adds to the cost. All of this might increase maintenance costs - Security releases will be required more often - There will be a need to patch not one but at least 2 systems

See also

[Vendor Support Center](../../../../business-and-strategic-alignment/system-modernizations/catalog-management-digital-commerce/vendor-support-center)