Automating NuxtJS Deployment to Heroku with CircleCI
Tags: devops, webdev
Published: 2020-09-09 15:10:11

(Published with permission from [Alvin Lee](https://medium.com/@alvinslee)).

Manually deploying a [NuxtJS](https://nuxtjs.org/) project is pretty easy when teams are small. However, as projects and teams grow they usually turn to CI/CD in their DevOps to automate their testing and deployment. This is when deployments—and the setup of deployments—can get complicated.

In this article, we’ll look at how to easily set up an automated CI/CD system for a NuxtJS project using [Heroku ](https://www.heroku.com/)and [CircleCI](https://circleci.com/). We’ll walk through all the details from setting up your GitHub repository to automating your tests to pushing your code changes. Then we’ll look at some suggested next steps.


## **What Is NuxtJS?**

First, here is a little background on NuxtJS, and why developers might choose it as part of their stack.

When [Vue.js](https://vuejs.org/) was first released, developers fell in love. With its simple template syntax and component structure, Vue.js makes it easy to spin up beautiful single-page applications (SPAs) with ease. However, developers soon realized that SEO was a problem. Since SPAs are deployed as a "shell" and the content isn't inserted until runtime, they learned [it could be difficult for search engines to accurately index the contents](https://vuejsdevelopers.com/2018/04/09/single-page-app-seo/). Because of this, developers needed a solution for handling server-side rendering (SSR) of their Vue.js applications.

Enter [NuxtJS](https://nuxtjs.org/).

NuxtJS is an open-source framework that combines all of the oft-used Vue.js libraries, bundling in [Vue Router](https://router.vuejs.org/), [Vuex](https://vuex.vuejs.org/), and [Vue Server Renderer](https://ssr.vuejs.org/) to provide Vue.js developers with the architecture for a smoother development experience. With NuxtJS, developers can get an SEO-friendly Vue.js application (with SSR) up and running within minutes. 


### Our Sample Project

Now let's look at our sample project and deployment setup. For this project, we’ll be using GitHub as our repository, Heroku as our host, and CircleCI as our CI/CD tool. Heroku is a PaaS solution that makes it easy to deploy and manage apps. CircleCI provides cloud-based CI/CD, running automated jobs in containers as soon as project code is pushed to GitHub. These jobs perform tests, send success or failure notifications, and then deploy built applications to cloud service environments. 

In the end, we want our outcome to be: **When we push our master branch to GitHub, an automated process will run all of our tests and then (if all the tests pass) deploy the application to Heroku.**

To that end, I'll take you through the following steps:

1. Set up a GitHub repository.
2. Create a basic NuxtJS application.
3. Write a few tests for our application.
4. Manually deploy the NuxtJS application to Heroku.
5. Configure CircleCI to run our test suite upon push to GitHub.
6. Configure CircleCI to deploy our application to Heroku upon passing tests.

Sound pretty simple? It will be. Let’s go!

 


### 1. Set up a GitHub repository

This tutorial requires an account with GitHub. A basic GitHub Free account will be sufficient.

We will set up a GitHub repository to house our project code. Later, we’ll connect our GitHub account with our CircleCI account. Whenever we push code to GitHub, this action will trigger a webhook to notify CircleCI to begin the CI/CD automation process.

On the “Your Repositories” page in GitHub, click on the “New” repository button.

Choose any name you’d like for this private repository. For this tutorial, we will call our repository `my-heroku-nuxt-app`.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yftpup5j2pqiujcvrk8s.png)


Click on “Create repository” to finish up. Then, clone the repository on your local machine. In the command below, make sure to use your own GitHub username. Notice that we are cloning the empty repository into a folder called `app`.


```
~/$ git clone git@github.com:[GITHUB USERNAME]/my-heroku-nuxt-app.git app 
Cloning into 'my-heroku-nuxt-app'... 
warning: You appear to have cloned an empty repository.
```


Excellent. Now that we have our `app` folder, let’s fill it up with a shiny, new NuxtJS application.


### 2. Create a NuxtJS Application

Just for your reference, we will be using Node.js `v10.20.0` throughout this tutorial. We’ll also be using <code>[yarn](https://yarnpkg.com/)</code> as our package manager.

From within your `app` folder, run the following command to create a new NuxtJS application:


```
~/app$ yarn create nuxt-app
create-nuxt-app v3.0.0
✨ Generating Nuxt.js project in .
? Project name my-heroku-nuxt-app
? Choose programming language JavaScript
? Choose the package manager Yarn
? Choose UI framework None
? Choose Nuxt.js modules None
? Choose linting tools None
? Choose test framework Jest
? Choose rendering mode Universal (SSR / Static)
? Choose development tools None
```


The interactive prompts will ask you to choose some options. You can chose whatever you’d like for “Project name,” but it’s important that you choose the options which I have shown in bold above.

By the way, if you would like to use `npm` instead of `yarn`, you can use the command `npx create-nuxt-app` instead of the `yarn create` command above.

Verify that your application works by running `yarn dev` from the command line. Your browser window should look like the following:


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ex650mazg5fsnnq94prd.png)


Great! Now that our application is up and running, let’s write some tests.


### 3. Create the Tests for Our NuxtJS Application

We’ll write tests just for our `index` page. Keep in mind that we are not planning to build anything beyond this basic boilerplate NuxtJS application. Our goal is to learn how to automate testing and deployment to Heroku.

For testing, we'll use [Jest](https://jestjs.io/), which is a popular JavaScript testing framework. It's easy to use and lightning fast. When we created our NuxtJS application above, we opted to bundle in Jest.

In the `app/pages` folder, create a new test file called `index.test.js`. We will write three tests which verify that our `index` page has certain content.


```
// FILE: ~/app/pages/index.test.js
```


```
import { mount } from '@vue/test-utils'
import index from './index.vue'

describe('index page', () => {   
  const wrapper = mount(index)  
  describe('app title', () => {    
    const element = wrapper.find('.title')    
    it('displays app title', () => {      
      expect(element.text()).toEqual('my-heroku-nuxt-app')    
    })  
  })  
  describe('links', () => {    
    const links = wrapper.find('.links')    
    describe('nuxtjs', () => {      
      it('contains link with correct text', () => {        
        const link = links.find('[href="https://nuxtjs.org/"]')        
        expect(link.text()).toEqual('Documentation')      
      })    
    })    
    describe('github', () => {      
      it('contains link with correct text', () => {        
        const link = links.find('[href="https://github.com/nuxt/nuxt.js"]')        
        expect(link.text()).toEqual('GitHub')      
      })    
    })  
  })
})
```


Let’s run our tests. We won’t be concerned about test coverage, so let’s include the `--coverage false` flag.



```
~/app$ yarn test --coverage false
  PASS pages/index.test.js
    index page
       app title
         ✓ displays app title (3 ms)
       links
         nuxtjs
          ✓ contains link with correct text (1 ms)
         github
          ✓ contains link with correct text (1 ms)Test Suites: 1 passed, 1 total
 
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 1.405 s, estimated 2 s
Ran all test suites.
Done in 2.21s.
```


Now, with tests working, let’s check in our code:



```
 ~/app$ git add .
 ~/app$ git commit -m "Create nuxt app with tests."
 ~/app$ git push origin
```


Nicely done. Now, we have a NuxtJS application with a few tests (which are passing), and we’ve pushed our `master` branch to GitHub.


### 4. Manually Deploy Our Application to Heroku

Before we build in automation, let’s walk through a basic, manual deployment to Heroku. This will help us understand what we’re doing later on when we automate deployment through CircleCI.

If you don't already have one, [set up a new account with Heroku](https://signup.heroku.com/). [The free plan](https://www.heroku.com/pricing) will work fine for this example. Then, log into  your account. From the dashboard, click on “New” and then “Create new app”.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fm2jr096kk66hnpororq.png)


Choose a name for your application. For this tutorial, we’ll go with the same name that we’ve been using thus far. Note that app names on Heroku must be unique across their system. So, it’s possible that `my-heroku-nuxt-app` is not available. If that’s the case, choose another name and take note of that for substitution as we go through this tutorial.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b3j7cfe7a76iay9lo8s5.png)


After the app has been created, you will see app deployment settings. We will be using the “Heroku Git (Use Heroku CLI)” method for deployment. We’ll use this as we deploy manually from our local machine command line, and then we’ll configure CircleCI to deploy via the command line, too.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a70aarc5uho3d9apqvhw.png)


Further down on Heroku’s deployment settings page, you will see a link with instructions for [installing the Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli#download-and-install) on your local machine. After installing the Heroku CLI, log into Heroku from the command line:


```
 ~/app$ heroku login
 heroku: Press any key to open up the browser to login or q to exit:
 Opening browser to https://cli-auth.heroku.com/auth/cli/browser/ ...
 Logging in... done
 Logged in...
```


Since our GitHub repository for this project already exists, we want to add the new Heroku remote. Make sure to use the Heroku application name that you chose above, if `my-heroku-nuxt-app` wasn’t available.


```
 ~/app$ heroku git:remote -a my-heroku-nuxt-app
```


Next, we want to set a few Heroku configuration variables to get our production deployment running. These instructions come from the [NuxtJS documentation on deploying to Heroku](https://nuxtjs.org/faq/heroku-deployment).


```
  ~/app$ heroku config:set HOST=0.0.0.0
  ~/app$ heroku config:set NODE_ENV=production
```


Now, we can push our NuxtJS application to Heroku for deployment.


```
 ~/app$ git push heroku
 Enumerating objects: 17, done.
 Counting objects: 100% (17/17), done.
 Delta compression using up to 4 threads
 Compressing objects: 100% (14/14), done.
 Writing objects: 100% (17/17), 167.78 KiB | 5.99 MiB/s, done.
 Total 17 (delta 0), reused 0 (delta 0), pack-reused 0
 remote: Compressing source files... done.
 remote: Building source:
 remote: -----> Node.js app detected
 remote: -----> Creating runtime environment
 remote: -----> Installing binaries
 remote: -----> Installing dependencies
 remote: -----> Build
 remote: -----> Pruning devDependencies
 remote: -----> Caching build
 remote: -----> Build succeeded!
 remote: -----> Discovering process types
 remote: -----> Compressing...
 remote: -----> Launching...
 remote: https://my-heroku-nuxt-app.herokuapp.com/ deployed to Heroku
 remote: Verifying deploy... done.
```


The command above is where all of the magic happens. Heroku detects that this is a Node.js app, and then it creates the proper deployment environment and installs all dependencies. After installing dependencies, Heroku also runs the `build` script command found in your `package.json` — this bundles all of the files needed for the client and for the server.

When we visit the URL for our Heroku app, we see our NuxtJS application up and running on the web:



![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b8wghuog29gpnsvt5cvn.png)


Just like that… our NuxtJS SSR application has been deployed to Heroku and is live. All that’s left to do is build in automated CI/CD through CircleCI.


### 5. Set Up Automated Testing with CircleCI

Create a new account at [CircleCI](https://circleci.com/) by clicking on “Log In with GitHub”.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jcc47qi2cx4fdk3ba9kq.png)


By logging in with GitHub, you authorize CircleCI to access all of your repositories. Within CircleCI’s dashboard, you’ll be able to select which of your GitHub repositories you want CircleCI to monitor.

From the CircleCI projects dashboard, you will see your GitHub repository named `my-heroku-nuxt-app` — click on the “Set Up Project” button to its right.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vg165kykozjwzvtacnmk.png)


CircleCI will choose a config template for you to start with (by default, it chooses the “Hello World” template). Shortly, we'll provide our own CircleCI config file. For now, select “Hello World” and click on “Start Building”:


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sl8xpzgqtvxsh8iydf6k.png)


This will pop up a modal saying that CircleCI will create a new branch and add this config template to that branch. But, we don’t need CircleCI to do this for us, so select “Add Manually”.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/axaodem1wa5c94qsz61f.png)


We’re told that we will need to create a `.circleci` sub-folder in our repository root folder, and then add a `config.yml` to that sub-folder. That’s what we’re about to do. We don’t need to download the template `config.yml` file, since we’re going to write our own. So, just click on “Start Building”.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ctm55p5rlmlhu1adln15.png)


When you set up your project for monitoring by CircleCI (and because CircleCI has been authorized to access your GitHub repositories), CircleCI will add a new public key to your GitHub repository’s settings.

CircleCI will immediately execute a workflow for this project. You’ll notice that this first build attempt fails. That’s because CircleCI is looking for the `config.yml` file in the `.circleci` sub-folder of the `master` branch of your project repository. That file doesn't exist yet, so let's create it now.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8u5x1fum5rld80iub5tb.png)


In your project root folder, create a new sub-folder called `.circleci`:


```
 ~/app$ mkdir .circleci
```


In that folder, create a new file named `config.yml`. We’re going to break this part of the tutorial into two separate parts. First, we’ll configure CircleCI to run our tests. Then, after we get that up and running, we’ll move on to configuring for Heroku deployment.

The contents of `config.yml` should be the following:


```
// FILE: ~/app/.circleci/config.yml
```



```
 version: 2.1
 jobs:
 run-test-suite:
 docker:
 - image: circleci/node:10.20.0
 working_directory: ~/project
 steps:
 - checkout
 - run:
 name: Fetch dependencies
  command: yarn install --no-progress --non-interactive --silent --pure-lockfile
 - run:
 name: Run test suite
 command: yarn test_ci
 - persist_to_workspace:
 root: ~/project
 paths:
 - .
 workflows:
 test-and-deploy:
 jobs:
    - run-test-suite
```


Let’s walk through what the above configuration does:



1. We define a new job called `run-test-suite`.
2. This job sets up an environment that supports Node.js with the version that we want.
3. Then, this job executes four steps. It checks out the repository code, installs dependencies, runs the test suite by running `yarn test_ci`, and then saves the current workspace folder to the machine, so that other jobs can still access the contents in its current state.
4. Our overall workflow, called `test-and-deploy`, only has one job in it: `run-test-suite`.

You may have noticed that CircleCI will call `yarn test_ci` from within our project folder to run the test suite. But we haven’t defined `test_ci` in our `package.json` script commands yet. We'll define that command with an [extra flag that tells Jest to run in continuous integration mode](https://jestjs.io/docs/en/cli). This affects how Jest handles [snapshot tests](https://jestjs.io/docs/en/snapshot-testing). Though we don't have any of those in our project right now, you'll want to keep this in mind in case you write snapshot tests for your projects in the future.. Since we’re updating our `package.json` file, let’s also turn off code coverage testing when running Jest:


```
// Excerpt from FILE: ~/app/package.json
```



```
  ...
  "scripts": {
  "dev": "nuxt",
  "build": "nuxt build",
  "start": "nuxt start",
  "generate": "nuxt generate",
  "test": "jest --coverage false",
      "test_ci": "yarn test --ci"
  },
  ...
```


Now, let’s add our new `.circleci/config.yml` file and our updated `package.json` file to git staging, and then commit them.


```
  ~/app$ git add .
  ~/app$ git commit -m "Add CircleCI config for testing, update package.json"
```


We push our new commit to `master`:


```
 ~/app$ git push origin
```


Within a few seconds, you should see a new pipeline entry on your CircleCI dashboard for this project. The `test-and-deploy` workflow for your project, on the `master` branch, will begin executing. It will execute the first and only job, which is to `run-test-suite`. Our tests should pass, and everything should be green.


### 
        
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5zi9fj9w3vjg5iy0k03s.png)



### 6. Set Up Automated Deployment to Heroku

For our final step, we want to configure CircleCI to deploy the code to Heroku when our tests pass.

To do this, we need to modify our `config.yml` file with the CircleCI configuration. We will make use of CircleCI’s [Heroku orb](https://circleci.com/orbs/registry/orb/circleci/heroku). CircleCI Orbs are reusable packages used to simplify configuration. CircleCI has a [huge registry of pre-built orbs](https://circleci.com/orbs/registry/) which simplify integration with third-party technologies (like Heroku). Our updated `config.yml` should look like the following (changes in **bold**):


```
// FILE: ~/app/.circleci/config.yml
```



```
version: 2.1
orbs:
   heroku: circleci/heroku@1.0.0
jobs:
run-test-suite:
docker:
- image: circleci/node:10.20.0
working_directory: ~/project
steps:
- checkout
- run:
name: Fetch dependencies
command: yarn install --no-progress --non-interactive --silent --pure-lockfile
- run:
name: Run test suite
command: yarn test_ci
- persist_to_workspace:
root: ~/project
paths:
- .
  deploy-to-heroku:
   docker:
    - image: circleci/node:10.20.0
   working_directory: ~/project
   steps:
     - attach_workspace:
        at: ~/project
    - heroku/deploy-via-git
workflows:
test-and-deploy:
jobs:
- run-test-suite
   - deploy-to-heroku:
       requires:
         - run-test-suite
       filters:
         branches:
          only: master
```


Here is what we’ve added:



1. In addition to the original `run-test-suite` job, we now have a `deploy-to-heroku` job. It also uses the same Node.js version and working directory.
2. The `deploy-to-heroku` job has two steps: First, it attaches to the workspace which we persisted in the previous job. Second, it calls the `deploy-via-git` command, which is defined in CircleCI’s Heroku orb. Essentially, it runs a Heroku CLI command similar to what we did when we manually deployed our application to Heroku above.
3. We have added this new `deploy-to-heroku` job to our `test-and-deploy` workflow, but we put some constraints on this job. First, it will only run after a successful run of the `run-test-suite` job. Also, it will only run if CircleCI is responding to a webhook for the `master` branch from our GitHub repository. This means that _all_ branches pushed to GitHub will result in a run of the `run-test-suite` job. But, _only_ the `master` branch will continue on with the `deploy-to-heroku` job. This make sense because we would, of course, only want to deploy the `master` branch to production.

Before this new CircleCI configuration will work, however, we need to update our CircleCI project settings with some environment variables related to our Heroku app deployment. In your CircleCI project settings, click on “Environment Variables”.



![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4b344rbyyoj2gqk8ipte.png)


We need to add two environment variables. The first is `HEROKU_API_KEY`, which can be found at your Heroku account settings.



![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xnv12syme99y750si8pa.png)


Click on “Reveal” and then copy/paste that value over in CircleCI as the value for the `HEROKU_API_KEY` environment variable.

The second environment variable we need in CircleCI is `HEROKU_APP_NAME`, which is the name of the Heroku app that you established when creating the app. (For our tutorial, it’s `my-heroku-nuxt-app.`)

After adding these two environment variables, your CircleCI project settings should look similar to below:



![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/00fy1ibglu10n7zr3em4.png)


Now that we’re all set, we can stage and commit our updated `.circleci/config.yml` file to git, then push to GitHub:



```
 ~/app$ git add .
 ~/app$ git commit -m "Update CircleCI config for Heroku deployment"
 ~/app$ git push origin
```


When we do this push to GitHub, here is what happens:



1. The new commit on the `master` branch is pushed to GitHub.
2. GitHub, receiving a new push, sends a webhook to CircleCI, indicating the name of the branch that just got a new commit.
3. CircleCI goes into action to launch the `test-and-deploy` workflow for this project.
4. The first job in the workflow is `run-test-suite`. All of the tests run and they all pass.
5. The `deploy-to-heroku` job runs next, since `run-test-suite` succeeded **and** the branch that just got a new commit is `master`.
6. Armed with the `HEROKU_API_KEY` environment variable (which gives CircleCI authorization to access your Heroku apps) and the `HEROKU_APP_NAME` environment variable (which lets CircleCI know _which_ Heroku app to deploy), the job pushes the code to Heroku.
7. Upon receiving the code pushed from CircleCI, Heroku builds the NuxtJS application and then starts up the server.

### Review and Wrap-Up


And there you have it. You have just set up complete CI/CD automation of your NuxtJS SSR application. By simply pushing your project to GitHub, your project tests will run, and your project will deploy to Heroku.

Let’s quickly review what we did: 



1. We set up a GitHub repository for our project.
2. We created a new NuxtJS application, using `yarn create nuxt-app`.
3. We wrote tests for our NuxtJS application, using Jest.
4. We manually deployed our project to Heroku, using the Heroku CLI.
5. We set up CircleCI to automate running our test suite whenever we push code to GitHub.
6. We set up CircleCI to automate deployment to Heroku whenever we push the `master` branch to GitHub and all of the tests pass.

### Next Steps


You now have the foundation you need to take this to the next level. From here, what might be some next steps you could take to build on this new knowledge?



1. Expanding on this bare bones NuxtJS application, you can build out new features accompanied by comprehensive tests. Now that you’ve automated test runs and deployment to Heroku, all you need to worry about is writing tests, writing code, and pushing to GitHub.
2. Or, if you already have an existing NuxtJS application, you can apply the Heroku deployment and CircleCI automation steps to your existing project. In less than an hour of your time, you can build in CI/CD.

Because deployment of NuxtJS SSR applications to Heroku is so quick and simple, you reduce the friction you’ll encounter when it’s time to go live. And, with CI/CD baked into your development process, you free up yourself and your team from another checklist of things to do when it is time to ship your code.