Decoupling configuration with Config Pages

2022.04.12
Druxt

Drupal exposes a lot of data that a decoupled app can consume, but in most cases it is limited to content rather than configuration; site title, slogan, logo among others are not readily available on a JSON:API build.

Today I'll be looking at the Config Pages module for Drupal, and the newly released Druxt companion module.

Decoupled configuration

One of the first Vue and Nuxt projects that I worked on was a calculator of sorts; a single page app (SPA) that used user input and calculated it against algorithms that used data and configuration provided by Drupal.

In that case it was a simple case of providing some "configuration data" in Drupal, and consuming that data in the SPA. While not groundbreaking in concept, having Drupal as an administrative UI for Nuxt powered applications and sites has so much potential.

While there may seem to be an emphasis that Decoupling Drupal is all about getting a better render engine for Drupal, the benefits Nuxt (or your preferred Frontend framework) get from Drupal are huge. And Decoupled Configuration is definitely one of the biggest. It was one of the reasons I started building Druxt.

While the Drupal JSON:API still doesn't provide anyway to access basic site configuration, there are many ways it can be done...

Config Pages

The Drupal Config Pages module has been my goto for Decoupled Configuration for many years now.

The module provides a fieldable Config Pages entity type, each Config Page type you create can only have one entity, and the data is exposed via the JSON:API if you have the appropriate permissions.

One Config Page type per application with any fields you want. It's a pretty straight forward solution to the issue.

Install and setup is pretty standard for a Drupal Entity type based module:

  1. Download the module: composer require drupal/config_pages
  2. Enable the module: drush en config_pages -y
  3. Create a Config Page type: /admin/structure/config_pages/types/add
  4. Add fields to the content type: /admin/structure/config_pages/types/manage/[TYPE]/fields
  5. Set the "View the [TYPE] config page entity" permission for the user role used be Druxt (anonymous in most instances): /admin/people/permissions/module/config_pages

Once the Config Page type is set up, you will also need to create the Config Page entity with the values you wish to provide to the frontend application: /admin/structure/config_pages/[TYPE]/edit

Consuming Config Pages

As the Config Page entity is just a regular Content Entity as far as Drupal is concerned, the data is accessible via the JSON:API and can be consumed in any number of ways by any number of clients and consumers. That's the joy of the JSON:API.

For Druxt, that means you could potentially load the data in with the DruxtClient:

const config = await druxt.getResource('config_pages--TYPE', uuid)

Or maybe even rendering it via the DruxtEntity component:

<DruxtEntity type="config_pages--TYPE" :uuid="uuid">
  <template #default="{ entity }">
    entity.attributes.field_NAME
  </template>
</DruxtEntity>

There's many solutions, it just depends on what your needs are.

For me, I wanted to have basic site settings as easily available to my site, no matter what component. Things like site name, slogan, social media accounts, colours and other customisations.

So I wrote a Druxt contrib module to take the data from the JSON:API and make it available in a Vuex store.

@druxt-contrib/config-pages

The module pulls in the configured Config Pages entities and populates a Vuex store during the Nuxt build process, made accessible via the Vuex store and $druxtConfigPages plugin.

Installation and setup is pretty standard Nuxt:

  1. Download the module: npm i @druxt-contrib/config-pages
  2. Add and configure the module in your nuxt.config.js file.
  3. The DruxtConfigPages module needs to have at least one page configured to operate.
nuxt.config.js
export default {
  buildModules: [
    '@druxt-contrib/config-pages',
    'druxt'
  ],
  druxt: {
    baseUrl,
    configPages: {
      pages: ['foo']
    }
  }
}

The module stores the data in its raw JSON:API format, but provides a Getter and Nuxt plugin to provide the data in a simplified format.

The configuration can be easily accessed via the $druxtConfigPages plugin:

<template>
  <h1>{{ $druxtConfigPages.get('foo').bar }}</h1>
</template>

The get method takes a single parameter, a reference to the Config Page entity and an optional reference to the field, delimited by a dot.

The field name excludes the Drupal field_ prefix.

In the following case, the data would come from field_bar from the config_pages--foo entity.

$druxtConfigPages.get('foo.bar')

The module and issue queue can be found on Github, and there's more features planned for future releases.

See the code Run in Gitpod

Alternatives?

I've been working on Decoupled project that uses Taxonomy among other things to provide multi-site functionality. One Drupal backend for many targeted consumers, Nuxt and other.

While I find the other side of the multi-site coin far more intriguing, one Druxt frontend for many Drupal backends, individually or concurrently, both use cases have a common need for consumer based configuration.

One of my future experiments with Druxt will be to look at the options of using the Drupal Consumers module alongside Drupal theme settings as a solution.

I'm curious to hear from others, have you dealt with Decoupled Configuration? What approach did you use? Let me know in the comments below.