Deploy an Eleventy Blog on Cloudflare pages with Strapi, MySQL, and Dokku on a Digital Ocean Droplet, part 4
Tags: mysql, strapi, dokku, devops
Published: 2022-06-17 01:58:34

- [Sidequest: Installing NVM in Powershell](#sidequest-installing-nvm-in-powershell)
- [Local Strapi backend Installation](#local-strapi-backend-installation)
- [Configuring our Local Strapi Backend](#configuring-our-local-strapi-backend)
- [Install GraphQL plugin and configure it locally](#install-graphql-plugin-and-configure-it-locally)
- [Creating a Collection Type for Articles](#creating-a-collection-type-for-articles)
- [Set roles and permissions on our Strapi API](#set-roles-and-permissions-on-our-strapi-api)
- [Database configuration with .env variables](#database-configuration-with-env-variables)
- [Testing the Strapi API Locally via GraphQL](#testing-the-strapi-api-locally-via-graphql)
- [Still to Come...](#still-to-come)

Welcome back!

╰(_°▽°_)╯

If you've been following along so far, you should have a Dokku instance on DigitalOcean that's been hardened from attackers (through [SSHD configuration and Fail2Ban static IP allow-lists](https://victorfeight.com/2022/03/secure_dokku_droplet-2/)). You should also have [a MySQL database server ](https://victorfeight.com/2022/05/secure_dokku_droplet-3/) installed onto the instance. ✔

In this post, we'll be installing Strapi CMS onto our local machine, and then allow Strapi to talk to our local MySQL instance. **Strapi** is an open-source content management system that eases managing a custom blog by enabling multiple users and fine-grained permissions over adding, editing, or deleting posts.

Once it's set up, it's as easy as making a new post with your username through Strapi's dashboard, and the file will get posted into our database automatically. We can turn on the GraphQL Playground and start querying for our posts.

**INFO:** Hostname is blocked by MySQL
Recently, I had an error when trying to connect to my remote mysql instance via MySQL Workbench.
The error was along the lines of: Host is blocked because of many connection errors.

At this point, I ssh'd into my machine with `ssh vic@apps.vicstech.xyz`, entered my dokku db instance with `dokku mysql:enter db` and while in the dokku db bash instance, entered `mysql -u root -p` followed by my db password.

Once in, I ran `FLUSH HOSTS;` to empty the host table. Apparently this happens if more than max_connect_errors occur successively for a given host, MySQL will block them. By flushing the tables, further connection attempts from the host are allowed.

## Sidequest: Installing NVM in Powershell

**TIP:** Strapi and Node versions
- Strapi won't run on NodeJS odd numbered versions - which are for developers. If you're not using LTS, you can temporarily change to use 14.16.1 with `nvm use 14.16.1` in an elevated powershell (assuming [nvm](https://github.com/nvm-sh/nvm) is installed).

![placeholder](https://victorfeight.com/img/fourth-post-20220615034520.png)


**TIP:** The easiest way to install `nvm` (Node Version manager) is to use Chocolatey. 🍫

1. Right Click Powershell icon in Start menu, select **Run As Administrator.**
2. Confirm chocolatey is installed by running `choco` command in a new elevated Powershell.

![placeholder](https://victorfeight.com/img/fourth-post-20220615160234.png)

3. If it's not installed, run the following command: `Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))`

4. Install `nvm` using `choco` by running: `choco -y nvm`
5. Open a **new**, _regular_ Powershell and install Node.js using nvm: `nvm install 14.16.1`

Great. We'll need to use Node 14.16.1 LTS in Powershell to ensure optimal Strapi and node compatibility.

## Local Strapi backend Installation

Create a new directory in your project and name is `strapi-dokku-backend`.

We're going to download the latest Strapi code onto our local machine before pushing it to our dokku instance.

In **_powershell_** with node 14.16.1 enabled, run npm/npx to install the Strapi project named 'strapi-backend' _locally_:

```bash
npx create-strapi-app@latest strapi-backend
```

- Choose manual Installation with arrow keys, press Enter
- Choose mysql as default database client
- Enter `db` as Database name.
- Enter `localhost` as Host.
- Enter `3306` as Port.
- Enter `root` as username
- Enter **password** used for local and remote root MySQL instances.
- Enter `N` for "Enable SSL connection" option

**INFO:** You can retrieve this password by sshing into `apps.vicstech.xyz` and running: `sudo cat /var/lib/dokku/services/mysql/db/ROOTPASSWORD` on your Dokku instance.

- If you've been following along, this password should now be associated with both the root user in your *local MySQL Workbench*, as well as the root user of our *remote MySQl instance* on the Dokku Digital Ocean droplet. If you entered something different as a local password, ensure DATABASE_PASSWORD in env is set to your remote instance ROOTPASSWORD, as that will take precedence in settings when Strapi is pushed onto dokku.

Next, run the following commands in the newly created strapi-backend directory:

```bash
cd strapi-backend
npm install
npm install strapi-admin
```

Powershell Output:
![placeholder](https://victorfeight.com/img/fourth-post-20220615154625.png)

Strapi command reference:

```text
Available commands in your project:

npm run develop
# Start Strapi in watch mode. (Changes in Strapi project files will trigger a server restart)

npm run start
# Start Strapi without watch mode.

npm run build
# Build Strapi admin panel.

npm run strapi
# Display all available commands.

# You can start by doing:
cd /mnt/c/Users/Vic/Documents/CODE_2020/ELEVENTY/strapi-dokku-backend/backend
npm run develop
```

## Configuring our Local Strapi Backend

We can go ahead and give it a whirl by running the following commands in the Strapi `strapi-backend` folder:

```bash
npm run build
npm run start
```

If everything went smoothly, you should get a _Windows Firewall notification_ asking to allow Strapi permission to serve its admin panel. Go ahead and **allow** it.
![placeholder](https://victorfeight.com/img/fourth-post-20220615155333.png)

As per [Strapi developer docs](https://docs.strapi.io/developer-docs/latest/getting-started/quick-start.html) quick start guide:

> Once the installation is complete, your browser automatically opens a new tab.
> By completing the form, you create your own account. Once done, you become the first administator user of this Strapi application. Welcome aboard, commander!
> 🥳

Complete this form to create the first **Administrator** user and click **Let's Start!**

![placeholder](https://victorfeight.com/img/fourth-post-20220615191600.png)

Now you'll have access to the Strapi **admin panel**. Navigate to `http://localhost:1337/admin` in your browser and you should see a success message:
![placeholder](https://victorfeight.com/img/fourth-post-20220615155911.png)

Now we've got a local Strapi server running (`strapi-backend`), configured to use MySQL with a database named `db` through port 3306.

## Install GraphQL plugin and configure it locally

We're going to install the GraphQL playground plugin and configure it to be used both locally or remotely.

Press `Ctrl-C` to cancel any running Strapi instance, then run the following in your `strapi-backend` project directory:

```bash
npm run strapi install graphql
```

Output:

Next, create a `plugins.js` file at `/config/plugins.js` (in your root `strapi-backend` directory):

```javascript
// path: ./config/plugins.js

module.exports = {
  //
  graphql: {
    config: {
      endpoint: "/graphql",
      shadowCRUD: true,
      playgroundAlways: true,
      depthLimit: 7,
      amountLimit: 100,
      apolloServer: {
        introspection: true,
      },
    },
  },
};
```

This will enable Strapi's **GraphQL playground** both locally and remotely (by default, it's turned off when deployed remotely), and set its endpoint to `/graphql`.

Output:
![placeholder](https://victorfeight.com/img/fourth-post-20220615163643.png)

Now, go ahead and rebuild Strapi with GraphQL installed, and run in develop mode:

```bash
npm run build
npm run develop
```

## Creating a Collection Type for Articles

**INFO:** Develop Mode
Develop mode allows us to add new collection types, and add fields to these new collection types.

We're going to create a data structure for our future blog content, add some entries and publish them so that the API for the content can be consumed. We do this by first creating collection types for `Article` with the content-type builder and create the fields to display when adding new entries.

- Navigate to **Content-Types Builder** under Plugins in the left-hand menu.
- Click the "**+ Create new collection type**" link
- Name it **Article** and click continue
- Add a **Text field (short text)** and name it **title**
- Click the "+ Add another field" button
- Add a **Rich Text field** and name it **content**
- Click the "+ Add another field" button
- Add a **Date field (date)** and name it **date**
- Click the "+ Add another field" button
- Add a **Text field (short text)** and name it **author**
- Click the "+ Add another field" button
- Add a **Text field (short text)** and name it **excerpt**
- Click the "+ Add another field" button
- Add a **Text field (short text)** and name it **categories**
- Click the "**Finish**" button
- Click the **Save** button and wait for Strapi to restart

![placeholder](https://victorfeight.com/img/fourth-post-20220615234116.png)

For more information, see the [Strapi Quick Start](https://strapi.io/blog/building-a-blog-with-11ty-and-strapi) guide.

## Set roles and permissions on our Strapi API

Next, we'll set roles and permission on our API and and then test querying our new posts in GraphQL.

Click Content Manager on the left menu.
Navigate to Article under Collection Types

Click "**Create New Entry**".

Fill in this information in the fields specified :
**title**: This is my lovely title
**content**: Hope you enjoy this great content.
**author**: vrfeight
**data** _pick a date of your choice_
**exerpt**: Short description
**categories**: test

Click **Save.**

In order to send requests to the Strapi API, we need public permissions on our articles to make them accessible.

- Navigate to **Settings** under **General** left menu.
- Navigate to **Roles** under **Users & Permissions Plugin**.
- Click the **Public Role**
- Scroll down under Permissions, find **Articles**
- Tick the **find** and **findone** boxes.
- Click **Save**

![placeholder](https://victorfeight.com/img/firefox_pJMAOi4ART.gif)

Now that we have a collection type with content, and permissions on the articles, we can finally test querying our API. 🎉

## Database configuration with .env variables

We'll finish setting everything up locally and confirm that local MySQL database querying works in the GraphQL playground.

Now, the next part's important, so sip your tea and roll up your sleeves. 🍵👕

**TIP:** Strapi local .env to remote settings
- Strapi uses an environment variable for Host, Port, APP_KEYS, and JWT_SECRET. When pushed to dokku, all these environment variables can be processed once configured on the server for our strapi-backend application, in addition to any other variables set in the `.env` file.

Our authentication strategy is based on the use of **Users & Permissions plugin** API access.

You will see in the `database.js` file's `module.exports` function there is a `connection` property holding an object with these configuration values (password will be unique, set upon Strapi installation):

```javascript
require("dotenv").config();

module.exports = ({ env }) => ({
  connection: {
    client: "mysql",
    connection: {
      // host: env('DATABASE_HOST', 'localhost' ),
      host: env("DATABASE_HOST", "localhost"),
      port: env.int("DATABASE_PORT", 3306),
      database: env("DATABASE_NAME", "db"),
      user: env("DATABASE_USERNAME", "root"),
      password: env("DATABASE_PASSWORD", "e754d62fcfk5f929"),
      ssl: env.bool("DATABASE_SSL", false),
    },
  },
});
```

The way this works, is that the local Strapi server will check the first argument value (ie `DATABASE_PORT` or `DATABASE_HOST`), and if these values are not found in the `.env` file, then use the second argument provided (`3306` or `localhost`)

A default Strapi `.env` file might look like this:
![placeholder](https://victorfeight.com/img/fourth-post-20220615024355.png)

Thus, we can start by testing our local server with this default `.env` file, and afterward add database variables in `.env` with information from our remote mysql server.

This will allow us to query from either a local database (MySQL Workbench) or our remote MySQL instance by commenting or uncommenting Database variables in `.env`.

**TIP:** Remote Dokku-mysql environment vars
- Later, when we push Strapi onto our remote dokku instance, we will use `dokku config` to set these same environment variables remotely, so that Strapi can pick up our remote dokku-mysql database. 😎

## Testing the Strapi API Locally via GraphQL

To query your data, navigate to `http://localhost:1337/api/articles/1`

You should get back a JSON response:
![placeholder](https://victorfeight.com/img/fourth-post-20220616014723.png)

Navigate to http://localhost:1337/graphql
Welcome to your personal GraphQL Playground. 🙂

Try this query:

```graphql
# Write your query or mutation here
query {
  article(id: "1") {
    data {
      id
      attributes {
        title
        content
        date
        author
        excerpt
      }
    }
  }
}
```

Output:
![placeholder](https://victorfeight.com/img/fourth-post-20220616022819.png)

Success. We've just successfully queried our local MySQL database through Strapi's GraphQL playground.

Great job. 🎈

## Still to Come...

Next time, we'll be deploying and confiuring Strapi on our Digital Ocean droplet, and we'll use the Dokku Plugin for LetsEncrypt to obtain a free certificate for our domain `vicstech.xyz` as well as our new strapi-backend subdomain we'll be creating.

Then, we'll practice querying our remote database with GraphQL, and start setting up an 11ty front-end to retrieve entries from the database .

Hope to see you soon.