Create and Deploy Serverless SPAs with Next.js on OpenFaaS or Knative (Google Cloud Run)

Johnny Eric Amancio
8 min readMay 21, 2019

--

TL;DR

I love to learn different topics around Software Engineering and I have been working as a Full Stack Engineer for the past years. At the same time I am very enthusiastic about Cloud, Containers, IoT and Machine Learning I also want to be able to develop Web Pages and push them to the Cloud. In this article I mix some of this concepts so we can quickly deploy Single Page Applications in a simple and cheap way.

Serverless

Serverless applications are based on the idea of functions/services that can be packaged as containers and integrated as a pool. They are meant to have an orchestrator that abstracts away the difficulties of managing the deployment of each application directly on VMs and enables lots of possibilities for automation.

Next.js

Today we already have several different ways of deploying static web apps and modern SPAs. At the same time, technology has been constantly evolving and so much experience obtained. Initially, applications were served mainly from the server side, later JavaScript started being used more and more to create Single Page Applications but both experiences had their trade-offs. Today, an alternative way is being explored, which is to use the best of both worlds. Next.js is a framework in this field which has done a great work around this topic.

Next.js makes it easy to create web applications with React and it takes care of several important aspects to make production ready web apps. It has the concept of pages as being an entrypoint url route that maps to a React component and is very flexible about configuration.

Regarding the Serverless concept, Next.js has recently released its release 8, which introduced a new feature to build a single page packaged as an independently deployable function. This is great as pages can now be built and deployed independently.

The way Next.js applications work is by providing the first render of the pages in the server. Then, after pages are loaded, they act as SPAs by being managed on the client side and fetch data from APIs when necessary.

OpenFaaS

I started experimenting with Open Source Serverless projects because I like the flexibility of containers to deal with all the different kinds of workloads. I researched about different projects but the one I appreciated most was the OpenFaaS project and I have been following it since then. In my opinion, it is great for being focused around containers and does not force us to use a specific function handler. Sure it has the concept of templates that turns things a lot simpler, but it also makes it very easy to customize a container image when necessary.

A few months ago, OpenFaaS published an article detailing how one could serve single page applications from a Serverless functions and I got very excited about it. After reading about the new serverless mode of Next.js and seeing some example usages, I started thinking about how I could leverage that with OpenFaaS as a simple way of pushing React components to the Cloud.

Knative and Cloud Run

Knative is a recent Open Source project from Google, Pivotal and other major companies created in order to provide base foundations for Serverless. It has three main components (build, serving and eventing) and integrates with Istio (Kubernetes Native Service Mesh).

Cloud Run was recently released by Google on their Cloud Platform. It uses Knative and Istio as its foundation and is a product where we can run stateless workloads packaged in containers at a very minimum cost as we only pay for the time the service is actively running.

Also, it has a free tier of 2 million invocations per month, that way you can get started for free.

OpenFaaS Next.js template

After the OpenFaaS blog post about Serverless SPAs and the release of the Next.js 8 with its new serverless mode I was tempted to figure out how I could integrate that with OpenFaaS. I was seeking a way to quickly create a React component and publish that to an url. As stated in my twitter @johnnyeric, I created an OpenFaaS template already setup with Next.js on serverless mode.

That way, we can generate the function based on this template via the OpenFaaS CLI, create a page and publish to an OpenFaaS gateway or deploy it to Google Cloud Run.

As of today, the template is not yet published on the OpenFaaS template store, but it is in process to be. It is being reviewed and for the moment you can use it directly from my repo.

Enough for introductions!

Let’s create a Serverless SPA

As stated in the title of this article there are two targets for deployment. In case of OpenFaaS, a deployment of the OpenFaaS Gateway will be required. Otherwise, it is possible to follow the deployment on Google Cloud Run. In both scenarios, the OpenFaaS CLI will be used.

Then, first make sure to have the CLI installed so we can start. Go and, follow the following post to get started with the CLI:

For simplicity, here it is one of the ways to install:

$ curl -sL https://cli.openfaas.com | sudo sh

Create a function using the template

After having the CLI installed, we can go ahead and create our Serverless SPA.

Get into a folder and execute the following:

$ faas template pull https://github.com/johnnyeric/openfaas-serverless-nextjs-template
$ faas new --lang serverless-nextjs <function-name>

The example function created from the template is ready to be deployed and you can test the deployment first and then customize. Also, it is already setup with emotion for styling but you can tweak the configurations or add packages as you wish.

Initially when created there are two example pages (Home and About) with links to each other.

In order for the links to work, it is important to define two parameters in the next.config.js, because depending on the environment the function runs it can have a url prefix.

Example configuration

Default (Will be enough for Google Cloud Run):

module.exports = {
target: ‘serverless’,
env: {
BASE_URL: ‘/’
},
//assetPrefix: ‘/function/<function-name>’
}

Example (Will be necessary for OpenFaaS Gateway):

module.exports = {
target: 'serverless',
env: {
BASE_URL: ‘/function/<function-name>’
},
assetPrefix: ‘/function/<function-name>’
}

Example function

This is what the function handler of the newly created function will look like.

const home = require('./.next/serverless/pages/index.js');
const about = require('./.next/serverless/pages/about.js');

module.exports = (context) => {
const contentPath = `${__dirname}/static`;
const defaultPath = `${__dirname}/static/404.html`;

context.servePages({
'/': home,
'/about': about,
});

// Everything not served as Next.js pages will be served as static
context.setCustomFolder(contentPath, defaultPath); // optional
context.serveStatic();
}

The index.js and about.js are just React components.

Deploy to OpenFaaS Gateway

At this point, if you have a gateway deployed, make sure to login to it in the CLI. If it is defined as default, the function definition will already point to the correct gateway, if not go to the YAML file and define it.

Also, it will be important to define the container image registry in the OpenFaaS YAML file. If using Docker Hub, then include your username at the beginning of the image name. Otherwise, define your own container registry, then make sure the OpenFaaS Gateway environment also has the credentials to this registry.

Example:

provider:
name: openfaas
gateway: <gateway-url>
functions:
<function-name>:
lang: serverless-nextjs
handler: ./<function-name>
image: <docker-hub-username>/<function-name>:latest

You can now build and deploy the function. To make things simpler, just run the up command that will do both.

$ faas up -f <function-name>.yml

And that's it!

Function page after deployment

My personal OpenFaaS Gateway showing the Function Page

Deploy to Google Cloud Run

First you can go and configure the gcloud CLI to use Cloud Run from the documentation.

On Cloud Run the image needs to be in Google’s container registry. You can just add the the registry address for your Google project at the function YAML file then only build the container instead of the up command.

Steps

  • First get your Google PROJECT_ID:
gcloud config get-value project
  • Then add to the image name like the example below:
provider:
name: openfaas
gateway: <gateway-url>
functions:
<function-name>:
lang: serverless-nextjs
handler: ./<function-name>
image: gcr.io/<PROJECT-ID>/<function-name>:latest
  • Then build and push the image:
$ faas build -f <function-name>.yml
$ docker push <image> # image is defined in the YAML file above

Next thing is to deploy the image to cloud run. It can be done from the gcloud CLI or the Google Cloud Console.

Deploying from the CLI

gcloud beta run deploy --image gcr.io/<PROJECT-ID>/<function-name>

As stated in the documentation:

When prompted, select a region (for example us-central1), confirm the service name, and respond y to allow unauthenticated invocations.

Deploy from the Google Cloud Console

It is very simple, just navigate to the Google Cloud Run panel, click on the Create service button and fill the fields on the page.

Steps

  • You may select the image previously pushed to the registry from a list.
  • Add a name to the service.
  • Activate the Allow unauthenticated invocations option so the page become public.
Google Cloud Run Create Service Page

Cloud Run Service page after deployment

Google Cloud Run Service Page

Access the pages

  • OpenFaaS

On OpenFaaS you can check the url when you click on the function, it will show an url like the following:

https://<openfaas-gateway-url>/function/<function-name>
  • Cloud Run

On Cloud Run when you open the service, it will show an url like the following:

https://<service-name>-<hash>.a.run.app

If you did not modify the pages after building from the template they will look like these:

Home Page
About Page

The designs are not meant to be elaborated, they are just something to get started with CSS-in-JS styles using emotion and an example of interaction through the links. And it is very easy to add new packages and create pages from scratch.

Conclusion

The technology has improved a lot in the recent years and for sure it will not stop. Actually it is very hard to choose from the many options we have, we can compose different tools and services in different ways to build incredible things. Having said that, this was my attempt to contribute to Open Source and add a simple way to use these awesome technologies together. I hope you find this template useful, and would appreciate any feedbacks to it.

--

--