[Deprecated] Hosting free Strapi CMS on Heroku [Building Personal Blog Website Part 1]
Tags: headlesscms, strapi, heroku, tutorial
Published: 2022-02-21 12:03:00

<aside>**As Heroku is phasing out the free tier I'm going to rewrite the first two parts of this guide. Here you can find the updated version: COMING SOON.**</aside>

_This is the first part of the "Building Personal Blog Website" series. In the series we'll setup a free CMS to hold our blog content on Heroku, create a React app with Next.js' static site generation and TailwindCSS to present the articles and host it on Netlify._

_All articles:
[Part 1: Hosting free Strapi CMS on Heroku](https://dev.to/hawelkam/hosting-free-strapi-cms-on-heroku-building-personal-blog-website-part-1-58gg)
[Part 2: Using Cloudinary with Strapi CMS](https://dev.to/hawelkam/using-cloudinary-with-strapi-cms-building-personal-blog-website-part-2-18o6)
[Part 3: Setting up a basic Next.js app locally](https://dev.to/hawelkam/setting-up-a-basic-nextjs-app-locally-building-personal-blog-website-part-3-13e6)_

Whether you want to create a simple blog or a portfolio website it is always a good idea to use a good CMS (Content Management System) behind. For quite some time Wordpress was a go-to solution, but now the choice is not so obvious. As an ambitious developer you can of course try to write your own CMS, but let's be frank - in most cases the best way is to use an existing one. That was my approach when I started to work on my personal website. 

My main interest were as follows:
- ability to change website's content without changing the code
- simple blog functionality
- out-of-the box GraphQL integration
- cheap site maintenance (preferably free)

After some time of "research" I chose Strapi CMS for the first three points and Heroku for the last one.

## Why Strapi?

- It's lightweight - compared to other CMSs Strapi is lightweight and really simple to set up.
- It's headless by design - I definitely prefer a headless CMS solution over a traditional CMS, it gives much more flexibility.
- It's written in JavaScript - this definitely helps with writing extensions/plugins if you use JS on a daily basis (which I am).
- It has really extensive docs - Strapi docs helped me in tons of situations, if you have an problem with Strapi it's almost guaranteed you'll find some help in the docs.

## Why headless?

Main reason - it's technology agnostic - traditional CMS requires you to write templates etc. in the technology the CMS is written. With headless this problem is gone. You utilize the REST/GraphQL endpoints to fetch the data and display it however you like.

## How to set this up?

For the most part I used a guide from [Strapi Docs](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment/hosting-guides/heroku.html). This guide shows how to create a new Heroku app with Strapi installed and connect it to the free-tier postgres database. I won't go into details here and repeat what's been said in this guide. But I have just a few additions to make it work properly.

So for starters go through [the guide](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment/hosting-guides/heroku.html) - but don't worry if at the end it doesn't work on Heroku (you get the application error).

The error says:

```
error: Middleware "strapi::session": App keys are required. 
Please set app.keys in config/server.js (ex: keys: ['myKeyA', 'myKeyB'])
```

That's why you need to make adjustments to production config. In the production config file created during the guide earlier (_env/production/server.js_) add few additional fields:

```
module.exports = ({ env }) => ({
  url: env("MY_HEROKU_URL"),
  proxy: true,
  app: {
    keys: env.array("APP_KEYS", ["testKey1", "testKey2"]),
  },
});
```

The `app.keys` is crucial. The data that you put in server.js doesn't matter. What matters is the value that you set in Heroku environment. You can do it directly through Heroku CLI.

```
heroku config:set APP_KEYS=someSecretValue1,someSecretValue2
```

Now when you commit and push changes and redeploy the app you should be able to see that Strapi server is running. Go to `https://[yourapp].herokuapp.com/admin` to set up the admin user and then log in. You should land in the dashboard.

![Strapi Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odzz6ufwqhsgs4tlk4u4.png)

As the app is in production mode you can't really make any adjustments here. Of course you can add a new post etc. but can't create a new content-type or add a new plugin. That's why you need to go into developer mode - make changes there and redeploy the app.

Doing this is pretty simple:

```
yarn develop
```

And when the app starts you'll need to set up admin account once again (it's stored in SQlite database, so it'll be gone after some time locally - but that's not a problem). Now when you log in you can create new content-types, configure plugins etc. 

Before starting creating content-types let's just quickly install a plugin that will be really useful later - GraphQL plugin which enables the default GraphQL endpoint for your CMS (you can skip it if you're ok with using standard REST calls, but I'll be using GraphQL in the React app). You can install it easily using yarn:

```
yarn add @strapi/plugin-graphql
```

And with that out of the way let's just create a blog post type.

In the content-type builder select a "_Create a new collection type_". 

![New collection type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/84vh44ttfw0eotu3pfpe.png)

And add some mandatory fields to it. In my case it's `title`, `content`, `cover`, `author` and `slug`.

![Post fields](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/282miyt1vo4whz5jlcqs.png)

For `author` it's important to select a proper relation:

![User to Post relation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfdcrwheu64q2bg3vp0w.png)

For slug remember to make it of type `UID` and select `title` as an attached field:

![Slug config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujouh8cd8d0j0324dfgw.png)

After you save you can commit all the new files - the schema files etc. will tell the production app what kind of content should be possible to create.

Now you are ready to redeploy the app using _Heroku CLI_:

```
git push heroku HEAD:main 
```

After the deploy is done you can open the app with `heroku open`, log in with the admin credentials and do some final configuration.

What you need to do is to give public access to posts. Do this by going into _Settings -> Users & Permissions Plugin -> Public -> Edit_ (icon).

![Permissions editing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mb0bpmzwjpkh3y3i14j.png)

While inside, give access to `find` and `findOne` for `posts`. This way you'll be able to query the CMS for the list of blog posts and for a specific one post (using `slug`).

![Posts permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6q1gc35z4odush9ko197.png)

Click _Save_ and now let's create an author and a dummy blog post to test the endpoint (_Content Manager -> User -> Create New Entry_ and _Content Manager -> Post -> Create New Entry_).

![Creating new post](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sodque1bnpvdlu76fzcn.png)

After saving a post you also need to publish it so it would be visible in the API.

To check if everything works use _Postman_ (or a similar app). 

![Postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lu0jugeyeegh4qjkd125.png)

Open up Postman and in the address field put `https://[yourapp].herokuapp.com/graphql/` and select _POST_ type of query. Then in the body of the query select _graphql_ type and type this:

```
query {
    posts {
        data {
            attributes {
                title
                content
            }
        }
    }
}
```
When you send this request, you'll receive the blog post that you just created a minute ago:

![GraphQL response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xgf01cpaydfg8akoide3.png)

As you can see the content is returned as markdown - it's much more efficient to send the data this way, but in our frontend app we'll need to convert it to HTML. We'll probably use something like [Showdown](https://github.com/showdownjs/showdown).

And that's it! We now have a hosted Strapi CMS that is accessible by both REST and GraphQL. Next step - hooking up Cloudinary to our CMS!