From Drupal 7 to Drupal 8 + Gatsby.JS (Part 1)
Blog running on Drupal 7 (or any other CMS)
Running a self-hosted blog has its challenges - and I bumped into some of those eventually. Well, rather than running out of ideas / motivation or time to come up with new articles, I eventually had some technical problems: Updates & Security - being the most important one ? ?.
If you’re using a self-hosted blog, you eventually will need to do constant updates. The more popular engine you’re using - the more risks of being hacked and the more up-to-date you have to be (especially if you’re running on Wordpress). I am kinda lazy, so eventually I skipped some big updates and my hosting got hacked. And this happened many times.
Many hacking waves are automated - they just crawl the web, identify Drupal websites and try commonly known exploits on them (that are documented everywhere), and if you’re not up-to-date, they breach-in. You can leave it hacked - but eventually someone will sell access to your hosting on the black-market and some shady things will start happening - in my cases I had some websites popping up on my VPS (I used DigitalOcean VPS) - and after making a backup of my website, I had enormous pleasure of destroying the Droplet with all the weird websites it had on it.
In modern days, there are ways one can automate this process, in example:
- use something like Pantheon - it comes with automatic core updates, however this still leaves room for contributed modules or plugins if you’re using Wordpress, which can also act as an attack vector
- use a CI tool, like CircleCI or Github Actions - you can have regular tasks that run daily, i.e. have proper Core Updates and Contributed Module Updates, maybe even run tests and if all goes well to deploy to production.
- something simpler but riskier would be - to have a cronjob that just runs updates directly on production - but you never know what will happen. Normally you want to test things first on your local environment and then replicate things on production.
However, to me, these options are way overkill - I don’t want to pay extra for Pantheon, nor I want to write a CircleCI (or Github Actions) script or a crontab + shell script. Also I don’t want to constantly run updates locally and then deploy those (that’s why I got hacked in first place).
Also, other than CMS’s vulnerabilities, there are also PHP’s vulnerabilities, Apache/Nginx’s vulnerabilities, ssh vulnerabilities - so you’d have to update the operating system itself and all the packages, then restart nginx, php-fpm, mysql and hope for the best.
Advantages of Headless Drupal + Static Front
Eventually I decided that I want to have a static front - running on a static engine, and the outputs of it can be hosted for free - either on some CDN, Github’s static pages, Netlify or even Amazon’s S3 Bucket.
Static front won’t have any need for updates (at least no security related needs), can’t be hacked, and I don’t have to worry about operating system updates, packages updates, etc.
So to sum up, it comes with a lot of advantages, and even some extra bonuses:
- it’s secured ?
- no crucial updates needed, low maintenance ?
- no monthly hosting payments ?
- no speed or pricing penalties for X concurrent visitors - if you have a popular article, it will result in same performance for everyone ?
- performance is amazing, static websites are crazy fast to load ?
- SEO - with some hiccups, the website usually performs pretty well on Google’s speed test ?
Many developers will be happy uploading simple markdown files and images directly to Gatsby.js (or any other static site engine) - but I want to have the experience of a CMS. I’d like to create / edit / delete content via UI, tag content, upload images, sort things via drag and drop, etc. For that purpose I decided to go with a combo of Drupal 8 (headless) and Gatsby.JS as a static front.
Using a CMS in a headless way, that feeds a static front, has many advantages (I like bullet points, I guess):
- you can place it under Apache password protection (user / password pair);
- you can keep it off the radar (not have it visible to Google and thus to automatic attacks);
- you don’t have to have 100% up-time (you can keep it on some server that can have internet issues / accidents);
- updating core or contributed modules / plugins is not mandatory or a security risk anymore, and if done, is easy and poses almost no risk.
I eventually settled on Drupal 8 and Gatsby.JS.
Why Drupal? Well, we know that Drupal is amazing, and the 8th version seems to be pretty solid. Also the 9th version is just polished off (minus the deprecated code) version of the Drupal 8. I also worked on manual migration of Drupal 7 to Drupal 8, I didn’t use the automated ‘upgrade’ path as I didn’t want to have half of the modules or other junk (plus, maybe there were some back-doors in old DB that I didn’t spot).
Why Gatsby.JS? Well, because it’s based on React and GraphQL, it’s super fast, it’s popular, it’s modern, it has a built bridge to Drupal 8, etc. I wanted to start learning React for a long time and Gatsby.JS was my introduction to it
Gatsby.JS
Gatsby.JS has many advantages and features, I won’t list or cover all of them, but I’ll just list some that were important to me:
- serverless - meaning that I can host it on Github Pages or Netlify - don’t have to pay for the hosting;
- performance - it’s blazing fast, and also scores high in SEO analytics due to speed;
- bridge to Drupal - it has a plugin that consumes Drupal’s API and populates GraphQL with data;
- PWA - progressive web app - you can transform your blog into PWA - people can add a shortcut on their phones to follow you, etc;
- Tech-stack - as I mentioned before, React and GraphQL is what I wanted. Gatsby also likes it when you split things into separate components, and use those in your code;
- Popularity - it’s important to use a ‘relevant’ tech, it’s rich in plugins, integrations and starters. Starters are nice to keep you off the ground fast, or to inspire you when you’re blocked.
Migration Process
Migration process, in my case, consisted of 5 parts:
- Creating a Drupal 8 website - locally. The git repo has both Drupal and Gatsby.JS in it ?.
- Data Migration ? - creating a custom migrate module, with custom YAML files that state how exactly old articles have to fit new Drupal 8’s structure.
- Picking a ‘theme’ (aka: template) - I don’t want to design a new theme myself, I’d rather pick something off the shelf.
- Implementing the ‘theme’ ? - slice the theme and package those as components in Gatsby, think through the pages / templates, check the queries, etc.
- Polishing ? - wire things together, use Netlify (in combination with Cloudflare), find a suitable host for Drupal, make SEO optimizations and test everything.
Before I dive into each step in a bit more details, I’d like to mention this repo - https://github.com/docksal/boilerplate-drupal-gatsby
I used it for my personal blog and it does make things a bit simpler - it’s a boilerplate that uses Docksal (docker containers) to spin-off an environment with a running Drupal + Gatsby.JS (and the bridge between them).
This is not my first Drupal 8 + Gatsby.JS implementation, earlier I did this setup myself but having this repo saved me some time and probably will save you ⌚ as well.
Creating a Drupal 8 website
This part probably went hand-in-hand with the 3rd part - picking a theme. I have analyzed various themes and the structure that I had in Drupal 7. I’ve picked the content-types and fields that I wish to have in Drupal 8 and created those - they differ just a bit from old implementation. Then I settled on an administrative theme (to manage my Drupal content in), picked some contrib modules and was ready to do the data migration.
Data Migration
In my case it was fairly simple - as my blog had a relatively simple structure. I wrote a custom Drupal 7 to Drupal 8 migration module to handle the data migration. I’ve run migrations and rollbacks until I was happy with the final result and seen all my data living in the Drupal 8 shell.
Just a note here - if you’re not happy with the standard Drupal 8’s “upgrade path” - you can just re-use its Drupal to Drupal migration classes.
In example, you can migrate Drupal nodes by using the d7_node source plugin, like this:
id: old_nodes label: Old Nodes migration_group: blog_posts migration_tags: - Drupal 7 source: plugin: d7_node node_type: article key: legacy process: # Node things you want to migrate. nid: nid
In this example ‘key’ is the database connection key used to connect to the old Drupal 7’s database. This way the migration process automatically figures out what is a Node, and a Node of the specific content-type and it also knows how to fetch the fields and their values, etc - magic :)
Picking a theme or template
You can build your own theme, but it will cost you some time - to design it, slice it, code it, etc. I wanted to look for something ready and responsive, so I will get it off the ground fast enough.
There are several options:
- Starters - Gatsby.js comes with a huge list of starters - these are ready-to-be-used Gatsby.JS implementations. This would have been the fastest approach to go with;
- JAM Stack Templates - there’s a huge list of JAM (JavaScript-API-Markup) templates (some of which are Gatsby.JS compatible), using these will also speed-up your development and save time;
- Use any static HTML5 site - in example - https://html5up.net/ - you can pick a template you like and then slice it up and package it into reusable components in Gatsby.JS (but will require a bit more time than the first 2 options).
In my case - I found the theme I liked on the JAM Stack Themes - https://jamstackthemes.dev/theme/gatsby-flexible/ with the source code as follows - https://github.com/wangonya/flexible-gatsby
Implementing the ‘theme’ & polishing
However I still had to adapt it to my liking - I also wanted to have tags, adjust the pagination a bit, have proper SEO snippets in-place, etc.
Also, this needs to fit into the Drupal infrastructure - what does this mean? This means that I had to create graphql queries, that know how many static pages to create, how many file-versions to create, how many tag-pages to create, etc.
Some of the highlights that I had to implement:
- Algolia-based search - I wanted users to have a dynamic search (ref);
- Disqus - I wanted users to interact with posts and leave comments, also to migrate my old comments as well (I was using disqus in 2012);
- SEO - wanted facebook / twitter preview images and text, also to have a feed, sitemap, etc.
- Tags - have 3 different categories of tags and wanted to automatically cover those;
- Code snippets - I had some complications in transferring the color-coded snippets to Gatsby;
- Images - images were tough as well, especially those embedded in the body / WYSIWYG of the existing Drupal content. There can’t be any references to Drupal-hosted images (otherwise they will reveal Drupal’s location) - and if you’re using Gatsby’s images, they are lazy-loaded with a sexy fade-in effect and stuff - so you need to implement a little trick to convert Drupal images into Gatsby’s.
- Integrations - Netlify, Google Analytics, etc.
I also was inspired by the Alexei’s code - and created GraphQL fragments to simplify my queries - but I still didn’t fully understand how these work, even though I spent quite some time on them:
import { graphql } from "gatsby"; // Document all fragments for paragraphs here. // // Include this file at least once so that webpack can pick it up, it's important to be picked up by Gatsby // during the build process so Gatsby can process all the fragments. export const query = graphql` fragment MediaField on media__image { file: relationships { field_media_image { localFile { preview: childImageSharp { fluid(maxWidth: 600) { ...GatsbyImageSharpFluid } } full: childImageSharp { fluid(maxWidth: 1400) { ...GatsbyImageSharpFluid ...GatsbyImageSharpFluidLimitPresentationSize } } } } } } fragment NodeCover on node__article { relationships { field_cover { ...MediaField relationships { field_media_image { localFile { ogFB: childImageSharp { fixed(width: 1200, height: 630, quality: 100, cropFocus: CENTER) { ...GatsbyImageSharpFixed } } } } } } } } fragment NodeTags on node__article { relationships { field_tags { name path { alias } } } } fragment NodeImages on node__article { relationships { field_media { relationships { field_media_image { localFile { childImageSharp { fluid(maxWidth: 300, maxHeight: 200, quality: 100, cropFocus: CENTER) { ...GatsbyImageSharpFluid } original { src width height } } } } } } } } `;
This allows you to use sexy queries like this:
export const pageQuery = graphql` query blogPageQuery($skip: Int!, $limit: Int!) { site { siteMetadata { title } } articles: allNodeArticle(limit: $limit, skip: $skip, sort: {order: DESC, fields: [created]}, filter: { status: {eq: true}}) { edges { node { internalId: drupal_internal__nid drupalId: drupal_id title created body { summary processed } fields { slug } ...NodeCover ...NodeTags } } } } `
Anyway, it’s a bit out of the scope of this article ?, I’ll prepare another blog-post with a bit more tech details and a published public repo so you can fork it or play with it as you wish ?
Outcome
A fast static website ?, that’s pretty flexible and dynamic to both its end-users (visitors) and to me - as an editor. It’s fast to get up and running, and also free to keep in running - as Netlify, Algolia and other services that I use are free.
If Netlify’s build time is not enough for your website or project, you can also use CircleCI (it offers more free build-time and the build process is a bit faster than Netlify) and you can publish the build from CircleCI to Netlify via Netlify CLI tool.
What’s next?
I’m planning to polish a bit my existing code and will release it as an open-source repo on Github, so you can play with it, or launch a similar looking blog if you want to.
Planning to dive a bit more into details on how to make the Drupal to Gatsby Bridge (and how to debug things with GraphiQL), what are the fragments and how to use those, how to create the components, how to use off-the-shelf react packages, and I’m also planning to release the CircleCI example on the build process (or in other words - how to have more “build minutes” and still use Netlify, for free).
If you’re interested - place a “?” reaction below, leave a comment, or just stay tuned ? for part 2.
Update (25 March 2021):
- Here's the code: https://github.com/Nikro/flexible-gatsby-d8-blog/
- Here's the Part 2 (deep dive / hands on): https://nikro.me/articles/professional/deep-dive-drupal-8-gatsbyjs-part-2/
Comments:
Feel free to ask any question / or share any suggestion!