Platform.sh User Documentation

How to deploy Gatsby with Drupal (Decoupled Drupal) on Platform.sh

Upsun Beta

Access our newest offering - Upsun!

Get your free trial by clicking the link below.

Get your Upsun free trial

Platform.sh maintains a template that you can quickly deploy, and then use this guide as a reference for the Platform.sh specific changes that have been made to Gatsby and Drupal to make it work. Click the button below to sign up for a free trial account and deploy the project.

Deploy on Platform.sh

Shared configuration Anchor to this heading

Your local clone of the template has the following project structure:

โ”œโ”€โ”€ .platform
โ”‚   โ”œโ”€โ”€ routes.yaml
โ”‚   โ””โ”€โ”€ services.yaml
โ”œโ”€โ”€ drupal
โ”‚   โ”œโ”€โ”€ # App code
โ”‚   โ””โ”€โ”€ .platform.app.yaml
โ”œโ”€โ”€ gatsby
โ”‚   โ”œโ”€โ”€ # App code
โ”‚   โ””โ”€โ”€ .platform.app.yaml
โ””โ”€โ”€ README.md

From this repository, you deploy a Gatsby app and a Drupal app. The code for each of them resides in their own directories. When deploying a single app project such as Gatsby, the repository needs three configuration files that describe its infrastructure, described below in detail. For multi-app projects, two of those files remain in the project root and are shared between Gatsby and Drupal. Each app keeps its own app configuration file (.platform.app.yaml) in its subdirectory.

Service configuration Anchor to this heading

This file describes which service containers (such as a database) your project should include. Gatsby does not require services to deploy, but Drupal does. So the following examples shows these service configurations:

# The services of the project.
#
# Each service listed will be deployed
# to power your Platform.sh project.

db:
    type: mariadb:10.4
    disk: 2048

cache:
    type: redis:5.0

files:
  type: network-storage:1.0
  disk: 256

Routes configuration Anchor to this heading

This .platform/routes.yaml file defines how requests are handled by Platform.sh. The following example shows Gatsby being served from the primary domain and Drupal being accessible from the backend subdomain.

https://api.{default}/:
  type: upstream
  upstream: "drupal:http"
  id: "api"
  cache:
    enabled: true
    cookies: ['/^SS?ESS/', '/^Drupal.visitor/']
https://www.api.{default}/:
  type: redirect
  to: "https://api.{default}/"

"https://www.{default}/":
    type: upstream
    upstream: "gatsby:http"
    primary: true

"https://{default}/":
    type: redirect
    to: "https://www.{default}/"

Drupal Anchor to this heading

The multi-app template has a single modification to Platform.sh’s standard Drupal template: the name attribute in Drupal’s .platform/services.yaml has been updated to drupal. This value is used to define the relationship between Gatsby and Drupal and in the routes configuration.

The only setup required to prepare the backend is to install a few additional modules that will configure the JSON API for consumption. In your Drupal directory, add the following dependencies.

composer require drupal/gatsby drupal/jsonapi_extras drupal/pathauto

The Pathauto module helps you assign alias paths for each piece of content on your Drupal site that can then be replicated on the frontend Gatsby site. For example, the Drupal alias /article/some-new-article is the same path you find that article at on Gatsby.

Gatsby Anchor to this heading

The frontend Gatsby app has a slightly different configuration from the basic Gatsby deployment. Below is the gatsby/.platform.app.yaml file that configures the app.

# The name of this app. Must be unique within a project.
name: gatsby

type: 'nodejs:14'

variables:
    env:
        NODE_OPTIONS: --max-old-space-size=1536
        GENERATED_VARS: 'deploy/platformsh.environment'

size: L

resources:
    base_memory: 1024
    memory_ratio: 1024

dependencies:
    nodejs:
        # yarn: "1.22.17"
        pm2: "5.2.0"

hooks:
    post_deploy: |
        # Verify the connection to the backend can be made with those variables.
        if [ -f "$GENERATED_VARS" ]; then
            # Source environment variables, build the frontend, and start the server.
            . $GENERATED_VARS
        else 
              printf "In %s, %s is not available, therefore I could not source" "web:commands:start" "${GENERATED_VARS}"
        fi
        # Gatsby clean on a RO-filesystem
        rm -rf .cache/* && rm -rf public/*
        npm run build -- --no-color        

web:
    commands:
        start: |
            # npm run serve -- -p $PORT --no-color

            if [ -f "$GENERATED_VARS" ]; then
                # Source environment variables, build the frontend, and start the server.
                . $GENERATED_VARS            
            else 
              printf "In %s, %s is not available, therefore I could not source" "web:commands:start" "${GENERATED_VARS}"
            fi
            APP=$(cat package.json | jq -r '.name')
            pm2 start npm --no-daemon --watch --name $APP -- run serve -- -p $PORT --no-color
            #pm2 start npm --no-daemon --watch --name $APP -- run develop -- -p $PORT --no-color



            # Maybe since we're foregoing the first deploy, yarn start is good enough.
            # if [ -f "$GENERATED_VARS" ]; then
            #     # Source environment variables, build the frontend, and start the server.
            #     # . $GENERATED_VARS
            #     npm run clean
            #     npm run build
            #     npm run serve
            #     # APP=$(cat package.json | jq -r '.name')
            #     # pm2 start npm --no-daemon --watch --name $APP -- preview -- -p $PORT
            # else
            #     # On the first deploy, display next steps page.
            #     node first_deploy.js
            # fi            


disk: 512

mounts:
    /.cache:
        source: local
        source_path: 'cache'
    /.config:
        source: local
        source_path: 'config'
    /.pm2:
        source: local
        source_path: 'pm2'
    public:
        source: local
        source_path: 'public'
    deploy:
        source: service
        service: files
        source_path: deploy

In particular, notice:

  • relationships

    Access to another service or app container in the cluster is given through relationships. In this case, one has been defined to the backend Drupal container using it’s name.

  • post_deploy

    Platform.sh containers reside in separate build containers at build time, before their images are moved to the final app container at deploy time. These build containers are isolated and so Gatsby can’t access Drupal during the build hook, where you would normally run the gatsby build command. Drupal isn’t available until after the deploy hook. So the Gatsby build is postponed until the post_deploy hook.

    To run gatsby build on-demand, or to trigger a rebuild from the backend when content is updated, define a runtime operation.

  • mounts

    There are consequences to postponing the Gatsby build, as you don’t generally have write access to the container this late in the pipeline. To allow Gatsby to write to public, that directory has been defined as a mount.

Additionally, there has been a change to Gatsby’s start command,web.commands.start. In the generic Gatsby template, the static files in public are served via the web.locations block, but that attribute is removed in the file below. Instead, two separate start commands are defined depending on which branch you are developing on. This has been included to support Live Preview and Incremental Builds. It isn’t required, but you can consult the section below for more information about enabling it.

You can then modify gatsby-config.js to read from the backend Drupal container through the drupal relationship defined above to configure the baseUrl attribute for gatsby-source-drupal.

This is facilitated by Platform.sh’s Config Reader library. So be sure to install this to the Gatsby dependencies first when replicating. When used, Gatsby pulls the information to communicate with the Drupal container on the current branch.


module.exports = {
  siteMetadata: {
    title: `Gatsby + Drupal on Platform.sh`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
    siteUrl: `https://github.com/platformsh-templates/gatsby-drupal`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,
    // `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-sharp`,
      options: {
        defaults: {
          formats: [`auto`, `webp`],
          placeholder: `dominantColor`,
          quality: 50,
          breakpoints: [750, 1080, 1366, 1920],
          backgroundColor: `transparent`,
          tracedSVGOptions: {},
          blurredOptions: {},
          jpgOptions: {},
          pngOptions: {},
          webpOptions: {},
          avifOptions: {},
        }
      }
    },
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        // This will impact how browsers show your PWA/website
        // https://css-tricks.com/meta-theme-color-and-trickery/
        // theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    {
      resolve: `gatsby-source-drupal`,
      options: {
        baseUrl: process.env.PUBLIC_DRUPAL_BASE_URL,
        // apiBase: `api`, // optional, defaults to `jsonapi`
        // secret: process.env.PREVIEW_SECRET,
        // basicAuth: {
        //   // username: process.env.BASIC_AUTH_USERNAME,
        //   // password: process.env.BASIC_AUTH_PASSWORD,
        //   username: process.env.DRUPAL_CLIENT_ID,
        //   password: process.env.DRUPAL_CLIENT_SECRET,
        // },
        fastBuilds: true,
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
}

Lastly, the Gatsby app itself needs to include GraphQL queries to handle the data coming from Drupal and create content pages. The most important files in the template you should consult are:

  • gatsby/gatsby-node.js

    Dynamically creates individual pages from the data source using Gatsby’s Node API. It retrieves all of Drupal’s articles (see post-install below) using the GraphQL query allNodeArticle. A page is created (createPage) with formatting described by the template file article.js below (component). A path is also defined for each article, in this case using an alias you will define within Drupal using the Pathauto module.

  • gatsby/src/templates/article.js

    The template file that defines how a single Drupal article should be formatted on Gatsby, retrieving the data from that article using the nodeArticle GraphQL query.

  • gatsby/src/pages/articles.js

    Generates previews of articles at /articles on the Gatsby site using the allNodeArticle GraphQL query.

Deploy and post-install Anchor to this heading

When you first deploy the template, the frontend Gatsby site will fail with a 403 error. Visit the `backend` subdomain of your site and finish the installation of Drupal. You don't need to set database credentials as they're already provided. After you have completed the installation, you need to enable the JSON API and Gatsby related modules and then set up aliases for your articles using pathauto. For detailed instructions, see the template’s post-installation instructions. Once you've finished, redeploy the project with the CLI command `platform redeploy` to view your Gatsby site, It's now pulling its content from a backend Drupal container in the same project.

Next steps Anchor to this heading

With Gatsby now deployed and pulling content from a backend Drupal application, there are a few things you may wish to change about your project going forward.

Shared application configuration Anchor to this heading

You can optionally combine the application configuration (.platform/services.yaml) for Gatsby and Drupal into a single configuration file. Like .platform/services.yaml and .platform/routes.yaml, this file is shared across the project and resides in the .platform subdirectory. You need to explicitly define the source of each application.

Multiple content sources Anchor to this heading

Gatsby supports pulling multiple sources into its build. This includes external services like Stripe and additional backend CMSs for different sets of content. As in this example with Drupal, you can branch off your repository and add an additional directory that contains the codebase for another backend. Then add the source plugin for that backend to gatsby-config.js.

Plan size Anchor to this heading

As mentioned previously, you should have at least a Medium plan for your multi-app projects. This size gives the project enough resources for all of your containers as well as the memory necessary to actually pull content from Drupal into Gatsby during its build.

Keep in mind that the increased plan size applies only to your production environment, and not to preview environments (which default to Standard ). As you continue to work with Gatsby and a backend headless CMS, you may want to upsize your preview environments.

Live preview and incremental builds Anchor to this heading

If you replicate the web.commands.start block in Gatsby’s .platform.app.yaml file above, you can enable incremental builds on your projects. Once you save an update to a piece of Drupal content on a preview branch, Drupal places a request to a dedicated /__refresh endpoint on Gatsby. Since Gatsby is running a development server on this non-production environment, this call causes Gatsby to retrieve content from Drupal once again, resulting in a near instantly updated article on the frontend.

To see how to enable this feature, consult the template’s README.

Is this page helpful?