Simple CI for Next.js projects with Apex UP + GitHub Actions

Michael Romanenko
6 min readAug 17, 2019

I love using Next.js for my Web projects! Apex Up makes deployment process extremely easy. Why not put to use GitHub Actions to make deployment of the project seamless and fully-automatic?

GitHub Actions

Powerful yet simple to use automation tool to do things in response to events, happening in your repo. For example: run tests or lint the code on push, notify you in a custom way when the new issue is created and other awesome useful things. And all this — with the power of Docker underneath.

As of August 2019 the feature is still in Beta, even though is enabled for many users. You may have to signup for the beta to try it out.

Apex Up

Lightweight wrapper around AWS Lambda to easily deploy scalable apps, APIs, and static websites. It leverages all the power of AWS, has minimal cli-interface and saves a lot of money on hosting web projects — with serverless architecture, which has usage-based pricing, to host small hobby projects is, basically, free of charge.

Next.js

A minimalistic framework around React to bring basic structure, SSR and other powerful and unobtrusive defaults for your Web app. For this article, I’ll use Up + Next.js example

Setting up Repository

Create a repository on GitHub and navigate to Settings tab:

There, in Secrets section add your AWS credentials, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (your IAM user should be configured with required policy settings for UP)

If you have Up Pro Subscription, which I highly recommend to get— it adds features like encrypted environment variables, active warming, rollbacks and supports development of the project — you can add UP_CONFIG variable with Up configuration as base64-encoded JSON. To obtain this run up team ci or up team ci --copy to copy it directly to your clipboard.

Your Settings -> Secrets should look like this. UP_CONFIG is optional

Deployment

If you are trying to set things up for the first time — I’d suggest to start by trying to deploy the project from local machine to make sure AWS and Up are configured correctly.

Make sure you have Up installed and variables with AWS credentials are available in your environment.

Start with example from https://github.com/apex/up-examples/tree/master/oss/node-next. Clone it, install dependencies, by runningyarn (or npm, if you prefer) in the project directory and simply run:

$ up

to deploy it to staging environment. To open the URL in your browser:

$ up url --open

If everything went well — we are ready to automate the deployment!

Creating Workflow

Workflows are custom automated processes that you can set up in your repository to build, test, package, release, or deploy any code project on GitHub.

Navigate to Actions section of your repository (Update to new Actions on prompt — the feature was recently updated)
For this example, we’ll Set up a workflow ourselves. We’re hacking today ;)
We’ll be presented with an editor with a sample workflow definition

Workflows are basically YAML files describing jobs and steps to perform, when they are executed.

For example: the simplest workflow for a Node.js project would be, when a code is pushed, checkout code from the updated branch, install dependencies and run npm test command. The workflow file can look like:

name: Test
on: push
jobs:
test:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- run: npm install
- run: npm test

Github Actions uses Action abstraction, to incapsulate functionality in pieces, share an re-use them across apps. There is even a Marketplace to make it easy to search for them, read docs and make maintainers happy by giving stars if you found them useful.

Actions don’t need to be published to the Marketplace to be able to use them — they just have to live in repository on GitHub. Most of them even don’t require to be configured. You can refer to them by simply specifying their repository with uses: keyword, like I did in the example above. For example, uses: actions/checkout@master — will execute action, published in repository checkout of GitHub user with name actions and use its default parameters.

Environment

Some actions require environment variables to be set, to provide a context for execution. There is a special env: keyword for that. Values can be assigned from Secrets section of the repository settings. For example:

env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}

Running multiple commands within a step

You can to combine multiple run calls into one, if you need to perform a series of things in one action.

For example, to install Yarn, run tests, Eslint, Flowtype and build the project, only if all checks went well (assuming you have appropriate npm scripts set up):

- name: Perform checks and build
uses: actions/setup-node@v1
with:
version: '10.x'
run: |
npm install -g yarn
yarn install
yarn run test
yarn run lint
yarn run flow
yarn run build

Up Deployment Workflow

Now, to perform deployment on code push, luckily, there is an official GitHub Action for Up, so we can add it to our Workflow and use it like any other action. However, for our example Next.js project, we’ll need to build it in proper Node.js environment before running up command.

So let’s supplement the Node.js example we’ve used before, to build the app, set up environment variables and deploy it to staging:

name: CI
on: push
jobs:
build:
name: Deploy staging build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Install dependencies and build the app
- uses: actions/setup-node@v1
with:
version: '10.x'
- run: npm install
- run: npm build
- name: Deploy the build via Up
env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
uses: apex/actions/up@v0.5.0
with:
args: deploy staging --no-build

Note how we use with:keyword to pass arguments to the command, which will be executed in the Up action, to skip the build in the Docker container, used by Up, because there is no Node available inside.

Branches and Stages

The job in the previous example will be executed for every push in every branch of the repository, and deploy our app to staging, even when we are working in a feature branch which is, probably, we don’t want.

So, for example, we can expand the on parameter to respond to pushes to master branch and deploy our app to production stage.

Plus, if you are a Up Pro user, you can add UP_CONFIG environment variable to enable Up Pro features in your workflow

Final action I use to build and deploy application to production, when master branch is changed:

name: Deploy production
on:
push:
branches:
- master
jobs:
build:
name: Deploy production build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Use Node.js v10.x
uses: actions/setup-node@v1
with:
version: '10.x'
- name: Install yarn and build
run: |
npm install -g yarn
yarn install
yarn run build
- name: Deploy the build via Up
env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
UP_CONFIG: ${{ secrets.UP_CONFIG }}
uses: apex/actions/up@v0.5.0
with:
args: deploy production --no-build

Summary

This makes the whole process of working on and continuously delivering Next.js project extremely smooth. Plus, you get all benefits of serverless architecture with its infinite scalability, unbeatable availability and really affordable usage-based pricing.

These are my current technologies of choice, which fit my needs really well. Feel free to adjust this example for your needs — I’m sure it’ll play nicely with any kind of Web project, with any framework underneath.

GitHub Actions is a very powerful tool, which can help automate many other things. Check out its official page for more info and keep in mind, that it is currently in Beta and some things might change by the release time.

--

--