Evolving Web: Decoupling Drupal with Gatsby

Planet Drupal - 9. Juli 2018 - 15:39

Gatsby is a really fast React-based static site generator. You can use it to create a static site, with content pulled from Drupal and other content management systems. 

Why Use Gatsby?

Unlike dynamic sites which render pages on-demand, static site generators pre-generate all the pages of the website. This means no more live database querying and no more running through a template engine. Performance goes up and the maintenance cost goes down.

Static site generators have been evolving over the last few years. Tools like Jekyll, Gatsby, Hexo, Hugo become more and more popular. They have been chosen by developers who want a simple website/blog solution. They need very minimal server setup and have a low maintenance cost. However, static site generators usually require writing content in Markdown, which is not a great authoring experience for most content editors.

On the other hand, content management systems such as Drupal and Wordpress can provide a very powerful back-end. Having a WYSIWYG editor and content types help editors to manage content more easily and systematically. However, maintaining a CMS requires hosting a web server and database, and opens you up to security vulnerabilities and performance issues.

Gatsby stands in between the simplicity and robustness of static site, and the versatile back-end of a content management system. Using Gatsby means that you can host the CMS in-house and publish content generated by Gatsby as a static website. The first thing you’ll notice about Gatsby is how amazingly fast it is.
 

How to Integrate Drupal and Gatsby

In this tutorial, we are going to put together a demo that pulls Drupal content into a Gatsby site. We’ll borrow content of this awesome blog post to create a list of coffee types in Drupal, then transfer the list content to Gatsby.

This goal can be achieved with 4 steps:

  1. Build a Drupal server
  2. Build a Gatsby site
  3. Fetch content from the Drupal server
  4. Publish the Gatsby site
1. Build a Drupal server

Let’s say we already have a Drupal 8 site installed. We’ll need to:

  • Create a content type name Coffee with three fields: Title, Body and Image
  • Turn Drupal into an API server by installing 2 modules jsonapi and jsonapi_extras.
  • Give Anonymous user permission to Access the JSON API resource list
  • Verify that the API server is working well by going to http://[your-site]/jsonapi as an Anonymous user. The page should show up with all information of your API server

Tips

  • If you use Chrome, use JSON Viewer Chrome extension to view JSON data in a better format
  • If you don’t set permission for Anonymous user to Access JSON API resource list, you’ll get error 406 - Not acceptable when trying to connect to Drupal from Gatsby
  • If you don’t have jsonapi_extras installed, you’ll get error 405 - Method Not Allowed when query data from Gatsby
2. Build a Gatsby Site

First, make sure you have node and npm installed on your computer. Verify it by typing node -v and npm -v into Terminal

node -v v10.1.0 npm -v 5.6.0

Install Gatsby’s command line tool

npm install --global gatsby-cli

Create a new Gatsby site and run it, I’ll call my Gatsby site coffees.gatsby

gatsby new coffees.gatsby cd coffees.gatsby gatsby develop // start hot-reloading development environment

By default, the new Gatsby site is accessible at localhost:8000

3. Fetch Content from the Drupal Server

At this step, we’ll be creating a new simple page /coffees that displays all the coffee types from the Drupal site.

Create the /coffees page

Create a new page in Gatsby is as simple as creating a new JS file. All Gatsby pages should be stored in /src/pages. In this case, we’ll create the file coffees.js in /src/pages and add the following code in coffees.js:

import React from "react" const CoffeesPage = () => ( Different types of coffee ) export default CoffeesPage

This simple code does one thing: create a new page at /coffees. The content of this page is a heading h1 with the text “Different types of coffee”

Query Drupal content using GraphQL

In order to pull data from Drupal 8 site, we’ll need to install the gatsby-source-drupal plugin

// in your coffees.gatsby folder npm install --save gatsby-source-drupal

Configure the gatsby-source-drupal plugin

// In gatsby-config.js plugins: [ ... { resolve: 'gatsby-source-drupal', options: { baseUrl: 'http://dcmtl2018-demo.server/', apiBase: 'jsonapi', // endpoint of Drupal server }, } ],

After adding the plugin configuration, the site should still be functioning. If Gatsby throws a 406 error, check the permission on the Drupal site; if Gatsby throws a 405 error, make sure module jsonapi_extras is enabled.

Build GraphQL to query all coffee nodes from Drupal

Gatsby comes with an in-browser tool for writing, validating and testing GraphQL queries named GraphiQL, and it can be found at localhost:[port]/___graphql, in our case it’s localhost:8000/___graphql

Let’s try querying all the Coffee nodes in this tutorial

After building the query successfully, let’s go back to the coffees.js file to execute the query.

export const query = graphql` query allNodeCoffee { allNodeCoffee { edges { node { id title body { value format processed summary } } } } } `

Then, update the const CoffeesPage to display the title and body content:

const CoffeesPage = ({data}) => ( Different types of coffee { data.allNodeCoffee.edges.map(({ node }) => ( { node.title } ))} )

Thanks to hot-reloading, we can see the sweet fruit of the work right after saving the file

So far, we have done:

  • Create an API server with Drupal and jsonapi, jsonapi_extras
  • Create a Gatsby site with page coffees.js that “reads” content from Drupal server

Let’s move the the last step of the tutorial: publish Gatsby site.

4. Publish the Gatsby Site

Gatsby names itself as a static static site generator, meaning its main purpose is to generate a bunch of static HTML, JS, CSS and images files. This action can be done by only one command:

gatsby build

Once finished, checkout /public folder to see result of your hard work along this long tutorial. Deploying your site is now simply copy/push contents in /public to server.

 

Conclusion

In this tutorial, we got to know how to:

  • Create a new Gatsby site
  • Install new plugin in Gatsby
  • Use GraphiQL to write, validate and test GraphQL query

I personally find that Gatsby is a good solution for setting up a simple blog. It’s easy to install, very fast, and requires zero server maintenance. In a future blog post, I’ll talk about how to integrate more complex data from Drupal into Gatsby.

+ more awesome articles by Evolving Web

DrupalEasy: DrupalEasy Podcast 211 - Tara King - Diversity and Inclusion Contribution Team

Planet Drupal - 9. Juli 2018 - 14:45

Direct .mp3 file download.

Tara King, Customer Success Engineer with Pantheon, Drupal Diversity and Inclusion leadership team, and a member of Core Mentoring Leadership team joins Mike Anello to talk about Drupal's Diversity and Inclusion Contribution Team - and how you can get involved.

Discussion DrupalEasy News Sponsors Follow us on Twitter Subscribe

Subscribe to our podcast on iTunes, Google Play or Miro. Listen to our podcast on Stitcher.

If you'd like to leave us a voicemail, call 321-396-2340. Please keep in mind that we might play your voicemail during one of our future podcasts. Feel free to call in with suggestions, rants, questions, or corrections. If you'd rather just send us an email, please use our contact page.

Suchmaschine Qwant: Neues Design zum 5. Geburtstag

heise online Newsticker - 9. Juli 2018 - 14:30
Die Suchmaschine Qwant legt viel Wert auf Privatsphäre – und auf ein gutes Aussehen: Zum 5. Geburtstag wurde das Design erneuert und für Smartphones optimiert.

Apple stopft WLAN-Lücken auf Macs unter Windows

heise online Newsticker - 9. Juli 2018 - 14:30
Mit einem Update sollen zwei Angriffspunkte in den Boot-Camp-Treibern behoben werden, mit denen Macs das Microsoft-Betriebssystem nutzen.

Schädlinge unterminieren Windows-Zertifikats-System

heise online Newsticker - 9. Juli 2018 - 14:30
Immer mehr Trojaner installieren eigene Root-CAs in Windows, um damit ihre Schadprogramme signieren oder Web-Seiten-Aufrufe manipulieren zu können.

Noch mehr Klagen gegen Apple wegen iPhone-Verlangsamung

heise online Newsticker - 9. Juli 2018 - 14:30
78 neue Kunden haben entschieden, den iPhone-Hersteller aufgrund der in iOS verbauten Leistungsdrossel zu belangen. Es ist nur eine von vielen Sammelklagen.

#TGIQF – das Newsticker-Quiz: Was ist diese Woche in der IT-Welt passiert?

heise online Newsticker - 9. Juli 2018 - 14:00
Tesla erreicht einen Meilenstein, der Upload-Filter scheitert und Dawn macht frische Aufnahmen. Haben Sie diese Woche aufmerksam mitgelesen?

Planetenweiter Sandsturm auf Mars: Opportunity noch inaktiv, Curiosity arbeitet

heise online Newsticker - 9. Juli 2018 - 13:30
Der immense Sandsturm auf dem Mars hat sich ausgeweitet und umspannt inzwischen den ganzen Planeten. Nicht alle Rover trifft das gleichermaßen.

Agiledrop.com Blog: AGILEDROP: Drupal Security Tips

Planet Drupal - 9. Juli 2018 - 12:55
Drupal is the leading enterprise web content management framework.  With this popularity, of course, comes the increased risk to security. While Drupal in itself, out-of-the-box is widely considered to be very secure, there are additional methods that one must definitely undertake in order to ensure their Drupal site stays attack-proof as much as possible.   Minimum Administrative Privileges In Drupal, users with administrative privileges have access to each and every section of the site. This, of course, means that administrative privileges in the wrong hands could prove to be the end of… READ MORE

Handelskrieg: China übt Vergeltung wegen US-Strafzöllen

heise online Newsticker - 9. Juli 2018 - 12:30
China reagiert auf US-Strafzölle und schnürt ein Gegenpaket in gleicher Höhe – eine Eskalation ist vorprogrammiert.

Outlook.com bekommt "besten Dark-Mode"

heise online Newsticker - 9. Juli 2018 - 12:00
Ein neuer "Dark-Mode" färbt Outlook.com bald dunkel ein. Die Designer haben hohe Ansprüche und versprechen den "besten Dark-Mode" aller Mail-Dienste.

Visual Studio Code 1.25 ist jetzt transportabel

heise online Newsticker - 9. Juli 2018 - 11:30
Die neue Version von Microsofts quelloffenem Editor Visual Studio Code kann dank Portable Mode einfach in andere Umgebungen mitgenommen werden.

ThinkShout: Automatic Page Generation with Custom Entity Routes

Planet Drupal - 9. Juli 2018 - 11:02

One of the most useful items in the Drupal 8 toolbox is the Paragraphs Module. By creating custom paragraph types, you can have much finer control over the admin and content creation process in Drupal.

A recent client of ThinkShout needed a content type (office locations) to include ‘sub-pages’ for things like office hours, services, and other items depending on the location. Most of the sub-page content was pretty simple, but they also needed to have direct links, be printable, and have the same header as the parent page. This ruled out an Ajax solution.

We’ve been using Paragraphs to make configurable content throughout the site, and since the sub-pages only have Title and Content fields, we thought they would be a good fit here as well. We then decided to explore the possibility of using custom entity routes to fulfill the other requirements.

To start, we created two additional view modes for the sub-page paragraphs called Sub-page and Menu link containing the Content and Title fields respectively. By keeping these fields in separate view modes, we make it much easier to work with them.

Next we created a custom module to hold all of our code, ts_sub_pages. In addition to the standard module files, we added the file ts_sub_pages.routing.yml, which contains the following:

ts_sub_pages.sub_pages: path: '/node/{node}/sub-page/{paragraph}' defaults: _controller: '\Drupal\ts_sub_pages\Controller\TSSubPagesController::subPageParagraph' _title_callback: '\Drupal\ts_sub_pages\Controller\TSSubPagesController::getTitle' options: parameters: node: type: entity:node paragraph: type: entity:paragraph requirements: _permission: 'access content'

This defines a unique system path based on the parent node ID and the paragraph entity ID. It would look like https://example.org/node/12345/sub-page/321. It also defines the call to the controller and the title_callback, essentially a location where we can create functions to manipulate the entity and its route. The options define the things to pass into the controller and title callback functions, and we also define access permissions using requirements.

One of the odd things about the controller and title_callback calls is that they look like a path, but are not. They have a predefined (and minimally documented) structure. You must do the following to make them work:

  • Create two folders in your module: src/Controller (case is important).
  • Create a file called TSSubPagesController.php - this must match the call.
  • Define a class matching TSSubPagesController in TSSubPagesController.php
  • Define a function matching subPageParagraph inside the TSSubPagesController class.

Example below. The names of the controller file, class, and function are up to you, but they must have the same case, and the file and class must match.

Digging into the TSSubPagesController.php file, we have a setup like so:

<?php namespace Drupal\ts_sub_pages\Controller; use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\Request; use Drupal\node\Entity\Node; use Drupal\paragraphs\Entity\Paragraph; /** * TS Sub Pages controller. */ class TSSubPagesController extends ControllerBase { /** * {@inheritdoc} */ public function subPageParagraph(Paragraph $paragraph, Node $node, Request $request) {

Here we have the namespace - this is our module. Note again that the src is taken for granted. Next are the Symfony/Drupal use statements, to pull in the classes/interfaces/traits we’ll need. Then we extend the ControllerBase class with TSSubPagesController, and define our subPageParagraph function. The function pulls in the $node and $paragraph options we defined in ts_sub_pages.routing.yml.

Now we can finally get to work on our sub-pages! Our goal here is to bring in the parent node header fields on every sub-page path. In the Drupal admin interface, go to ‘Manage Display’ for your content type. In our case it was /admin/structure/types/manage/location/display. Scroll to the bottom and under ‘Custom display settings’ you’ll find a link to ‘Manage view modes’. We added a mode called sub-page, and added all of the fields from our Location’s header.

Now we can bring that view of the node into the sub-page using the subPageParagraph function we defined above:

<?php public function subPageParagraph(Paragraph $paragraph, Node $node, Request $request) { $node_view_builder = \Drupal::entityTypeManager()->getViewBuilder('node'); $node_header = $node_view_builder->view($node, 'sub_page'); $paragraph_view_builder = \Drupal::entityTypeManager()->getViewBuilder('paragraph'); $paragraph_body = $paragraph_view_builder->view($paragraph, 'sub_page'); return ['node' => $node_header, 'paragraph' => $paragraph_body]; }

We get the node and paragraphs using getViewBuilder, then the view modes for each. The node’s ‘sub-page’ view mode contains all of the header fields for the node, and the paragraph ‘sub-page’ view mode contains the paragraph body. We return these, and the result is what looks like a page when we visit the base paragraph url of /node/12345/sub-page/321. The title is missing though, so we can add that with another small function inside the TSSubPagesController class (we call it using the _title_callback in ts_sub_pages.routing.yml):

<?php /** * Returns a page title. */ public function getTitle(Paragraph $paragraph, Node $node) { $node_title = $node->getTitle(); $paragraph_title = $paragraph->field_title_text->value; return $node_title . ' - ' . $paragraph_title; }

Now we need to build a menu for our sub-pages. For this we can just use the ‘sub-pages’ paragraph field on the parent node. In the admin display, this field is how we add the sub-page paragraphs, but in the public-facing display, we use it to build the menu.

First, make sure you include it in the ‘default’ and ‘sub-page’ displays as a Rendered Entity, using the “Rendered as Entity” Formatter, which has widget configuration where you need to select the “Menu Link” view mode. When we set up the Paragraph, we put the Title field in the ‘Menu Link’ view. Now the field will display the titles of all the node’s sub-pages. To make them functional links, go to the ‘Menu Link’ view mode for your sub-page paragraph type, make the Title a ‘Linked Field’, and use the following widget configuration:

Destination: /node/[paragraph:parent_id]/sub-page/[paragraph:id] Title: [paragraph:field_title_text]

Next we need to account for the fact that the site uses URL aliases. A node called ‘main office’ will get a link such as /locations/main-office via the Pathauto module. We want our sub-pages to use that path.

We do this by adding a URL Alias to the sub-page routes on creation (insert) or edit (update). In our module, we add the following functions to the ts_sub_pages.module:

<?php /** * Implements hook_entity_insert(). */ function ts_sub_pages_entity_insert(EntityInterface $entity) { if ($entity->getEntityTypeId() == 'paragraph' && $entity->getType() == "custom_subpage") { _ts_sub_pages_path_alias($entity); } } /** * Implements hook_entity_update(). */ function ts_sub_pages_entity_update(EntityInterface $entity) { if ($entity->getEntityTypeId() == 'paragraph' && $entity->getType() == "custom_subpage") { _ts_sub_pages_path_alias($entity); } }

These get called every time we add or update the parent node. They call a custom function we define just below. It’s important to note that we have a custom title field field_title_text defined - your title may be the Drupal default:

<?php /** * Custom function to create a sub-path alias. */ function _ts_sub_pages_path_alias($entity) { $sub_page_slug = Html::cleanCssIdentifier(strtolower($entity->field_title_text->value)); $node = \Drupal::routeMatch()->getParameter('node'); $language = \Drupal::languageManager()->getCurrentLanguage()->getId(); $nid = $node->id(); $alias = \Drupal::service('path.alias_manager')->getAliasByPath('/node/' . $nid); $system_path = "/node/" . $nid . "/sub-page/" . $entity->id(); if (!\Drupal::service('path.alias_storage')->aliasExists($alias . "/" . $sub_page_slug, $language)) { \Drupal::service('path.alias_storage') ->save($system_path, $alias . "/" . $sub_page_slug, $language); } }

This function gets the sub-page paragraph title, and creates a URL-friendly slug. It then loads the paragraph’s node, gets the current language, ID, and alias. We also build the system path of the sub-page, as that’s necessary for the url_alias table in the Drupal database. Finally, we check that there’s no existing path that matches ours, and add it. This will leave old URL aliases, so if someone had bookmarked a sub-page and the name changes, it will still go to the correct sub-page.

Now we can add the ‘Home’ link and indicate when a sub-page is active. For that we’ll use a custom twig template. The field.html.twig default file is the starting point, it’s located in core/themes/classy/templates/field/. Copy and rename it to your theme’s template directory. Based on the field name, this can be called field--field-sub-pages.html.twig.

The part of the twig file we’re interested in is here:

{% for item in items %} <div{{ item.attributes.addClass('field__item') }}>{{ item.content }}</div> {% endfor %}

This occurs three times in the template, to account for multiple fields, labels, etc. Just before each of the for loops, we add the following ‘home’ link code:

{% if url('<current>')['#markup'] ends with node_path %} <div class="field__item active" tabindex="0">Home</div> {% else %} <div class="field__item"><a href="{{ node_path }}">Home</a></div> {% endif %}

Next, we make some substantial changes to the loop:

{% set sub_text = item.content['#paragraph'].field_title_text.0.value %} {% set sub_path = node_path ~ '/' ~ sub_text|clean_class %} {% if url('<current>')['#markup'] ends with sub_path %} <li{{ item.attributes.addClass('field__item', 'menu-item', 'active') }}>{{ sub_text }}</li> {% else %} <li{{ item.attributes.addClass('field__item', 'menu-item') }}><a href="{{ sub_path }}">{{ sub_text }}</a></li>

Here, sub_text gets the sub-page title, and sub_path the path of each sub-page. We then check if the current url ends with the path, and if so, we add the active class and remove the link.

And that’s it! The client can now add as many custom sub-pages as they like. They’ll always pick up the parent node’s base path so they will be printable, direct links. They’ll have the same header as the parent node, and they will automatically be added or deleted from the node’s custom context-aware menu.

Hmm, maybe this would make a good contributed module?

Elektro-Fahrräder: Fahrradbranche lehnt generelle Versicherungspflicht ab

heise online Newsticker - 9. Juli 2018 - 10:30
Die EU-Kommission plant, die Versicherungspflicht auch auf Pedelecs auszuweiten. Die Fahrradbranche befürchtet Verluste bei E-Bikes und kündigt Widerstand an.

Zwei Jahre Pokémon Go – und kein Ende abzusehen

heise online Newsticker - 9. Juli 2018 - 10:30
Zwei Jahre nach dem Start spielen immer noch über fünf Millionen täglich Pokémon Go. Das ist auch gezieltem Taktieren zu verdanken.

Samsung verdient weniger als erwartet, Smartphone-Geschäft schwächelt

heise online Newsticker - 9. Juli 2018 - 10:00
Der Gewinn beim Smartphone-Marktführer fällt etwas geringer aus als zuvor und als erwartet. Am meisten Geld verdient Samsung mit seinem starken Chip-Geschäft.

Test Dell XPS 15 2-in-1: Notebook mit Vega-Grafik und "Magnetschwebetastatur"

heise online Newsticker - 9. Juli 2018 - 9:30
Das Dell XPS 15 2-in-1 wandelt zwischen Notebook und Tablet. Angetrieben wird es vom Core i7-8705G, der Intel-Prozessor und AMD-Grafik vereint.

Selbstlernender Algorithmus beherrscht Rubik's Cube

heise online Newsticker - 9. Juli 2018 - 9:00
Forscher an der University of California haben eine Software entwickelt, die sich selbst beibringt, den Zauberwürfel zu knacken.

Open Photonik Pro: Neues Förderprogramm für Maker und Gründer

heise online Newsticker - 9. Juli 2018 - 9:00
Das Forschungsministerium will neue Photonikprodukte fördern und hat dafür ein Programm aufgelegt, das auf die Zusammenarbeit von Makern und Firmen setzt.

Bikesharing: Obike ist offenbar pleite

heise online Newsticker - 9. Juli 2018 - 9:00
In Singapur hat ein vorläufiger Insolvenzverwalter die Regie beim Bikesharing-Anbieter Obike übernommen. Tausende Kunden warten auf ihre Kaution.