I built my personal website using Gatsby, Strapi, Heroku, Netlify and Github Actions

Sanjar Kairosh
December 5th, 202010 min read
Photo By Sean Lim on Unsplash

I wanted to build my website and have some semblance of a CI/CD pipeline in place to push changes from local development to production easily. I wanted to have a frontend and a backend deployed separately, communicating with each other.

I set up the Gatsby frontend site on Netlify, while the content was managed by Strapi deployed on Heroku. I also added customized webhooks to Strapi to trigger builds of the site via Github Actions workflows. Building the site on Github saved me precious minutes of build time on Netlify (You only get a limited amount of build minutes per month).

By writing this I hope that the reader can also set up his environment to comfortable build and manage a personal website.

This is not a tutorial on Gatsby or Strapi. There is a great tutorial about it here that does a better job than I will ever do.


How To Install and Get Strapi Running locally

In order to run Strapi locally, Node.js version 12.x is a minimum while 14.x is recommended. You will also need Npm version 6.x.

You can find much of the documentation on how to install Strapi here if you want to skip this part.

Use Npm to install a Strapi project.

npx install create-strapi-app project-name --quickstart

Strapi currently supports SQLite, PostgreSQL, MySQL, MariaDB, and MongoDB as databases. --quickstart the option tells Strapi to use SQLite, which is all I frankly need for local development with Strapi.

To run Strapi run

npm run develop

Go to http://localhost:1337/admin and create an account. You will be redirected to the admin page. You now have a working local Strapi instance.

Once you are on the admin page you can create and manage content for the website, whether it is blog posts, about me information, or work experience, anything you want really.


Gatsby Setup

The Gatsby documentation is pretty neat in explaining how to install and use Gatsby.

Similar to Strapi, ensure Node.js, NPM, and Git are installed on your system.

Install the Gatsby CLI tool

npm install -g gatsby-cli

To create a new gatsby project you can draw upon gatsby “starters” (partially built sites with some default configuration).

gatsby new gatsby-project-name https://github.com/gatsbyjs/gatsby-starter-hello-world

The Github URL points to a repository that has starter code for a Gatsby project. To run Gatsby locally type

gtasby develop

Your local Gatsby instance will be live by default on http://localhost:8000 .


Gatsby retrieving content from Strapi

For Gatsby to fetch the content from Strapi, install the following Gatsby plugin — Gatsby has a rich plugin system.

npm install gatsby-source-strapi

The plugins are defined and configured in the gatsby-config.js file. For now, the URL of the local Strapi instance is sufficient. Also specified is the type of contents to retrieve.

plugins : [
    {
        resolve: `gatsby-source-strapi`,
        options: {
            apiURL: `http://localhost:1337`,
            queryLimit: 1000, // default to a 100
            contentTypes: [`article`, `experience`],
            singleTypes: [`about`],
        }
    }
]

As I mentioned before I won’t cover how to query the Strapi documents using graphql as it is extensively covered here, or any other in-depth tutorial about Gatsby.

Your Gatsby can now query content from Strapi and work with it on the frontend. Stop your current local Gatsby project from running. Ensure the Strapi instance is still running, and execute the following

gatsby build

A folder named public will appear in the root of your project. This is your static site compiled into one folder with all the content from Strapi.

You can then drag and drop this folder into Netlify to deploy.


Deploy public folder on Netlify

Create an account on Netlify. It is a straight-forward process.

Once you have an account with a team you can create and deploy a new site by simply dragging and dropping your public gatsby folder into the drop area under Sites.

Your site will take a bit to build. Once finished you can open your live site via the Netlify generated long URL. You have a working site on your hands!

I was repeating this process every time I built my site after updates. The drag and drop capability is amazing, but I wanted an even better experience, one more geared toward a developer, where my site would update after me pushing code to a Github repository.


Deploying Strapi To Heroku

Netlify gives the option to build the site from Git. But in order to do that, I had to have a deployed version of Strapi API from which to query content on each Gatsby build. http://localhost:1337 would not make any sense to the code on the remote Github repository.

The Strapi official website has documentation on how to deploy Strapi to various platforms such as Heroku, AWS, Azure, DigitialOcean, and Google App Engine. I chose Heroku since I found it simplest to use.

The Heroku deployment instructions are fairly extensive and you could skip this part altogether. However, I did find some nuances with the setup which I wanted to elaborate on.

You must have git installed and a free Heroku account. Also, make sure to install the Heroku CLI.

You can log in to Heroku from the command line

heroku login

This will redirect you to the Heroku website where you will be authenticated to use your account. Return to the command line after you are done.

Update your .gitignore file to include package-lock.json . Including it may create some issues on Heroku. I didn’t bother to learn why. I trust Heroku!

You have to initialize your Strapi project as a git project, which will be managed by Heroku. Make sure you are in the project folder.

cd project-folder
git init
git add .
git commit -m "First Commit"

Now you can create a Heroku app

heroku create

This command is typically only used on an initialized git repository. It creates a remote git repository on the Heroku app to which you can push changes from your local repository.

You now have a free app on Heroku (a user gets a total of three free apps I believe) configured with your local repository. For Strapi to work on Heroku you need to use PostgreSQL or MongoDB. PostgresSQL it is.

Install the Postgres add on (Add ons on Heroku seem to be the equivalent of plugins to Gatsby).

heroku addons:create heroku-postgresql:hobby-dev

The add on exposes the database credentials into an environment variable which you can access via the Heroku config

heroku config

This should print a URL that reads as “postgres://USERNAME:PASSWORD@ HOST:PORT: DATABASE_NAME”.

Since Strapi requires database credentials to connect to the database, you will need to deconstruct the variables from the above URL.

Install the pg-connection-string module with npm:

npm install pg-connection-string --save

If you take a look at the file ./config/database.js you will see that it currently is

env gives you access to the environment variables. To use Strapi both locally and in production on Heroku, we need to configure the database.js such that different databases are used based on process.env.NODE_ENV .

We use the parse function from the pg-connection-string module if the node environment is a production environment, which is the case on Heroku. That parse function then deconstructs the Postgres database URL and sets the different environment variables on the Heroku config object.

The production database connection uses the config object to access the different variables necessary to connect to the Postgress database.

When Strapi is running locally, NODE_ENV is equal to “development” and the database connection is determined by the devConnections function.

This part was key for me to setup Strapi.

I was finally able to push changes from development to production mode with both Strapi instances working separately. It is important to note that the content data you create locally will not migrate to Heroku since you are using two completely different databases.

We also need to do

heroku config:set NODE_ENV=production

Finally, we need to install the pg module.

npm install pg

Add all these changes to git and push to the Heroku remote git to update the app.

git add .
git commit -m "configured pg database for Heroku"
git push heroku master

You can open your app from the command line

heroku open

You will again need to create a new admin user for production Strapi and then you can manage content on Strapi deployed on Heroku.

To build new content types or update some configuration, add changes in development, and then push them to the Heroku app. This is because Strapi disables the content-type builder on production mode for security reasons.

With Strapi deployed on Heroku. Gatsby can start building from it.


Build Site on Netlify From Git And use Heroku Strapi API

Link your Gatsby project to a remote git repository on Github. If you go to your Netlify account home page there will be a green button that says ‘New Site from Git’. Once pressed you will be prompted to select a repository from either Github, Gitlab, or Bitbucket. I used Github.

Once you setup a Github app for Netlify and successfully connect a Github repository, Netlify will create this new site for you and automatically build and deploy it.

Since we want to use Strapi on Heroku we will have to update our gatsby-config.js file.

module.exports = {
    // ...
    {
        resolve: `gatsby-source-strapi`,
        options: {
          apiURL: process.env.NODE_ENV
            ? "https://blossoms-badlands-91879.herokuapp.com"
            : `http://localhost:1337`,
          queryLimit: 1000, // Default to 100
          contentTypes: [`works`, `blogs`],
          singleTypes: [`about`],
        },
    }
    // ...
}

As you can see, depending on the node environment, Gatsby will communicate with the corresponding Strapi instance.

Now, every time you update your Gatsby project locally and push the changes to your remote Github repository, a build, and deployment of your site on Netlify will be automatically triggered.

This is great. But what happens when you enter new content on your Heroku Strapi. Your site on Netlify has no means to know about it. It will not update with new content. You would have to manually push something from your local Gatsby to your Github repository in order to trigger a re-build of the site.

That could prove annoying and it is the reason I decided to implement webhooks with Strapi.

Webhooks, with Strapi, to trigger Gatsby site build on Netlify

On your site on Netlify, go to Site Settings. In the options on the left select Build & Deploy. On that page keep scrolling down until you see Build hooks and click add build hook. Give the hook a name and keep the default Github branch from which it will build.

Once created you will see an HTTPS address. Every time your content on Strapi updates a webhook on Strapi should send an HTTPS request to the build hook. This will automatically trigger a rebuild of the site and update with new content.

To add a webhook on your Heroku Strapi, go to the admin panel and select Settings in the menu on the left. Select Webhooks under Global Settings. Click Add New Webhook. Give the webhook a name, and add the address of the Build Hook you just created on Netlify.

Under Events check the Entry box. This tells Strapi to send a request to the build hook every time content is created, edited, or deleted.

That’s neat. And I thought my job was done until I realized that Netlify has a limited number of build minutes. I wanted to save those minutes and I realized I could do that after stumbling upon this great tutorial.

Save Build Time on Netlify By Building On Github Using Actions

You can build your site on Github using Github actions, and simply deploy the built site to Netlify. You will spend zero build minutes on Netlify. And Github Actions build is unlimited if I am not mistaken!

To do that you need to create a .github/workflows/main.yml file that looks like this:

name: Netlify

# Controls when the action will run. Triggers the workflow on push request, or repository dispatch
on:
  push:
    branches: [main]
  repository_dispatch:
    types: [created]
    

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    #  The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the jobs
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Setup Node.js environment
        uses: actions/setup-node@v2.1.2
        with:
          version: 12.x

      # Runs a set of commands using the runners shell
      - name: Build the site
        run: |
          npm install --production
          npm run productionbuild
      - name: Deploy To Netlify
        uses: nwtgck/actions-netlify@v1.1.11
        with:
          publish-dir: "./public"
          production-branch: main

        env:
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}

Every time you push code to your Gatsby repository on Github, this workflow will be executed.

I won’t go into too much detail about how Github Actions work.

But on every push of code, the node environment will be set up. The site will be built in that environment and deployed to the Netlify site. You will need to obtain your Netlify site id, as well as a Netlify authentication token for authorization.

The Netlify site id is found under Site Settings in the Site Information box as API ID. Copy this value and save it as a secret in your Github repository with the name NETLIFY_SITE_ID.

To access the Netlify auth token go to user settings when you click on your profile avatar top right of the Netlify page. On the menu on the left select Applications and click New Access Token under Personal Access Tokens. Give a description of your taken and copy the value. Save it as a secret in your GitHub repository.

Finally, under Build & Deploy, click Edit Settings under Build Settings and select the Stop Builds option. This will ensure that when your Github Gatsby repository deploys the built code to Netlify, Netlify does not build it either.

We are almost there now. What happens when your content on Heroku Strapi updates?

It will send an HTTPS request to the Netlify build hook notifying it to rebuild the site. But Netlify can’t do that anymore since we stopped all builds on that site.

Customized Webhooks To Trigger Repository Dispatch

You can write lifecycle methods for your content on Strapi. If you check your folder structure for local Strapi, you will find an api folder that contains the models for your content. For example, I had a work content type and so there was a folder for it inside the api folder.

Inside my work folder, I had the folders config, controllers, models, and services. Inside models, I opened the work.js file which looked like this

"use strict";
const axios = require("axios");

/**
 * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#lifecycle-hooks)
 * to customize this model
 */

module.exports = {};

If you want to send a repository dispatch upon any update to the content, you can do so in Strapi model lifecycle hooks. For example the afterCreate the hook will be created every time after the content instance is updated.

"use strict";
const axios = require("axios");

/**
 * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#lifecycle-hooks)
 * to customize this model
 */

module.exports = {
  lifecycles: {
    async afterCreate(data) {
      const url =
        "https://api.github.com/repos/sanliverpool13/gatsby-netlify/dispatches";

      const token = process.env.GITHUB_TOKEN;

      const headers = {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        Accept: "application/vnd.github.v3+json",
      };

      const axiosResult = await axios.post(
        url,
        { event_type: "created" },
        { headers }
      );
    },
  },
};

A repository dispatch is an event you can trigger by sending an Axios request to your specific Github repository api URL. You will need to create a Github token that you should send with the request. You can also send a payload with the request, specifying the name of the event type for your repository dispatch.

You can include the repository dispatch in your Github Actions workflow such that the Gatsby site is built upon any repository dispatch with event type “created”.

name: Netlify

# Controls when the action will run. Triggers the workflow on push request, or repository dispatch
on:
  push:
    branches: [main]
  repository_dispatch:
    types: [created]
    

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    #  The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the jobs
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Setup Node.js environment
        uses: actions/setup-node@v2.1.2
        with:
          version: 12.x

      # Runs a set of commands using the runners shell
      - name: Build the site
        run: |
          npm install --production
          npm run productionbuild
      - name: Deploy To Netlify
        uses: nwtgck/actions-netlify@v1.1.11
        with:
          publish-dir: "./public"
          production-branch: main

        env:
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}

Thus every time there is an update to your content on Strapi, the appropriate life cycle hook is triggered. This in turn sends a repository dispatch event to your Gatsby repository, which sets in motion the build of your site. Once the site is built, it is deployed to Netlify and you have the updated Site live.

The lifecycle hooks need to be added locally and then pushed to the Heroku instance of Strapi.


I hope you enjoyed reading this! Please let me know if I missed anything or was not clear in explaining a step!.

Blogs

copyright ©2021 all rights reserved, Sanjar Kairosh
illusrations by icons8