• Home
  • Contact
from-drupal-to-gatsby

Migrating from Drupal to Gatsby, ThemeUI, and MDX

Jesus Manuel Olivas
August 15, 2020

We recently launch the site for Octahedroid, the agency I co-founded together with Omar Aguirre and Jorge Valdez. For our agency site we are using Drupal as a Decoupled CMS and Gatsby as frontend strategy.

For my personal blog I decided to simplify this and remove the Drupal backend part and use markdown files but including support for JSX components.

This is not the first time our team work in Gatsby and MDX projects. We did an implementation in the past using this technology for one of our clients Pantheon you can take a look to the following image to see this, and click to it, to visit their documentation pages.

Why MDX

MDX is an authorable format that lets you seamlessly write JSX in your Markdown documents.

As mentioned above we built the company site using Gatsby, but not only that we created the site using the amazing Theme-UI project and implemeting a Gatsby theme containing the React components. Therefore we decided to reuse those components in the MDX blog implementation and only extract and migrate content from Drupal to MDX files.

The main repo was done implementing a monorepo approach and it was organized in the packages and starters directories.

The packages directory

The packages directory contains the presentation theme gatsby-theme-octahedroid with the reusable JSX components, and the data layer themes gatsby-theme-octahedroid-drupal and gatsby-theme-octahedroid-markdown with the Drupal and MDX implementations.

packages ├── gatsby-theme-octahedroid ├── gatsby-theme-octahedroid-drupal └── gatsby-theme-octahedroid-markdown

The starters directory

The starters are very basic gatsby projects implementing the data layer themes.

starters ├── jmolivas.com └── octahedroid.com

How basic it was the starter implementation?. I will list files and content to ilustrate this.

The package.json file

{ "name": "jmolivas-website", "version": "1.0.0", "main": "index.js", "author": "Octahedroid <hq@octahedroid.com> (@OctahedroidHQ)", "private": true, "dependencies": { "gatsby": "^2.23.11", "gatsby-theme-octahedroid-markdown": "^1.0.0", "react": "^16.11.0", "react-dom": "^16.11.0" } }

The gatsby-config.js file

module.exports = { siteMetadata: { title: `jmolivas`, description: `Hi, I am Jesus Manuel Olivas ...`, author: `@jmolivas`, siteUrl: 'https://jmolivas.com', }, plugins: [ { resolve: `gatsby-theme-octahedroid-markdown`, options: { root: __dirname, themePath: `${__dirname}/theme`, }, }, ], }

The content directories

content/ ├── assets ├── blog └── pages

The files implementing custom features (Theme Shadowing)

src/ ├── gatsby-theme-octahedroid │   └── components │   └── Logo │   └── index.js └── gatsby-theme-octahedroid-markdown ├── footer.js └── menu.js

Migrating content from Drupal to Markdown files

The only Drupal task here it was to enable the GraphQL module (yes the site was a few years old and it was still using JSON:API).

The GraphQL query

Since the site has been inactive more than a year ago, it was no Drupal site on production, only an instance running on my local machine. So instead of deploying a Drupal site to production, the decision we took was to install the GraphQL module and execute a GraphQL query to extract the node pages information and copy/paste to a gist file.

{ articles: nodeQuery( filter: { conditions: [ { operator: IN, field: "status", value: ["0", "1"] }, { operator: EQUAL, field: "type", value: ["article"]}] }, sort: [ { field: "nid", direction: DESC } ], limit: 1000 ) { entities { ... on NodeArticle { title path { alias } created: entityCreated createdFormatted: entityCreated(format: "F d, Y") published: entityPublished fieldImage { entity { entityLabel ... on MediaImage { fieldMediaImage { url title alt } } } } fieldResume body { value } fieldTags { entity { entityLabel entityUrl { path } } } } } } }

The Javascript code

For the file creation process we decided to use this simple Javascript code, instead of PHP to iterate and resolve this task faster.

import fs from 'fs' import axios from 'axios' const ctaOptions = ['jamstack', 'agency', 'cms'] const ctaLength = ctaOptions.length axios .get( 'https://gist.githubusercontent.com/jmolivas/.../raw/.../articles.json' ) .then((res) => res.data.data.articles.entities) .then((entries) => { for (const entry of entries) { if (!entry.published) { continue } let keywords = '' for (const tag of entry.fieldTags) { keywords += ` - '${tag.entity.entityLabel.toLowerCase()}'\n` } let body = entry.body.value // Remove <br> body = body.replace(/<br>/g, '') // Add video embed const videoReplacer = (matched) => `<VideoEmbed url="${matched.slice(10, -1)}" />` body = body.replace(/\`youtube: http.+?\`/g, videoReplacer) // Replace image paths const imageReplacer = (matched) => `(../assets/images/${matched.slice(30, -1)})` body = body.replace( /\(\/sites\/jmolivas\/files\/images\/.+?\)/, imageReplacer ) // Add gist embed const gistReplacer = (matched) => { const values = matched.slice(6, -1).split('#') return `<Gist gist="${values[0]}" file="${values[1]}" />` } body = body.replace(/\`gist:.+?\`/g, gistReplacer) const content = `--- title: "${entry.title}" path: '${entry.path.alias}' date: '${entry.created.slice(0, 10)}' description: '' ${ keywords ? ` keywords: ${keywords}` : `keywords: []` } cta: '${ctaOptions[Math.floor(Math.random() * ctaLength)]}' header: credits: '' author: name: 'Jesus Manuel Olivas' link: '' ariaLabel: '' image: src: '../assets/${ entry.fieldImage.entity.fieldMediaImage.url.split('/files/')[1] }' alt: '${entry.fieldImage.entity.fieldMediaImage.alt}' --- ${body}` fs.writeFile( `content/blog/${entry.path.alias.slice(1)}.mdx`, content, (err) => { if (err) { console.log(err) } } ) } }) .catch((err) => console.log(err))

Wrapping up

Migrating content and implementing the blog using MDX it was easier than we expected because of:

  • Reusing our previously created JSX components and extending the presentation theme gatsby-theme-octahedroid save us a lot of time.
  • Using Javascript to generate the MDX files instead of custom PHP code it was faster to code and try.
  • Copying the GraphQL query result to a gist file instead of deploying the full Drupal site save a lot of time since there was no need to setup or configure anything.
  • Copying image assets directly to the repository was faster than code an export or import process.

Yes, implementing the simplest solutions for the tasks and reusing what we have done already make us faster, efficient and productive.

Big shout to our youngest Octahedroid team member Carlos Dinarte for taking care and executing all this work.

Ready to embrace the JAMstack revolution?

Build fast and secure sites and apps delivered by pre-rendering files and serving them directly from a CDN, removing the requirement to manage or run web servers, databases and worry about traffic spikes.
Work with us!