Building a Serverless GraphQL App with Next.js, Hasura and CloudRun

Geoff Strickland
Tactable Blog
Published in
10 min readSep 10, 2019

The source code for this blog post can be found here

At Tactable we’ve been heavily leveraging CloudRun since its release and want to show how it can be used to deploy a modern application utilizing some of the most exciting technology available. The technology used (Serverless, Docker, CloudRun, Hasura, GraphQL, Next.js) might seem like buzzword overkill but this article will demonstrate an example of how we use it internally to speed up development while increasing scalability using a repo that we’ve developed as one of our application starter templates.

Next.js

Next.js is an isomorphic react framework that that has server side rendering, hot module replacement, automatic code splitting, static page exporting and so much more. The production builds created by Next.js are heavily optimized and performant and the ecosystem has tons of great documentation, tutorials and starter templates.

CloudRun

CloudRun is a new offering from Google that allows you to deploy serverless containers fully managed on CloudRun or inside GKE. When your application isn’t receiving any traffic, it’ll be frozen so you only pay for the resources you use.

Applications deployed on the managed version of CloudRun have automatic SSL and can autoscale up to 1,000 container instances which can be increased via GCP support. Between Google’s free-tier and the first 180,000 vCPU-seconds free (per billing period), you can get some serious usage before ever paying a cent.

Hasura

Hasura is an application that uses any existing (or new) Postgres database and exposes a GraphQL server and management dashboard. Hasura drastically reduces the barrier to entry as well as complexity when starting a new GraphQL project or adding GraphQL to an existing project. With the Hasura console you have the ability to create database migrations, use the GraphiQL explorer, configure extremely granular role based access and much more.

Just show me how to run it already!

The application comes with a fully configured docker-compose.yml (for development only), you will need to have docker installed. There’s just 2 steps to get up and running once you have the repo cloned.

  1. Add the following to your /etc/hosts file
127.0.0.1        host.docker.internal

2. Run the following command from the application root folder

docker-compose up 

This will start the following services:

  • Postgres database @ localhost:5432
  • Hasura console @ localhost:9695
  • GraphQL server @ localhost:8080
  • Next.js app @ localhost:8081

The application will take a few seconds before it’s ready. If it’s your first time running the app it will have to create a new Postgres database and the GraphQL server waits for the Postgres database to be up and running before applying migrations and starting up.

Once all of the services are up and running, navigate to localhost:8081. If the GraphQL server is still starting up it will show error loading but once the server is ready it will have some dummy data available from a test seed migration file in the hasura/migrations folder.

You can also connect to the database (username “hasura”, db “postgres” at localhost:5432) to check out the test data, add and edit some rows. During development, all data is stored inside the postgres/postgres-data folder. If you want to blast the database just delete that folder and it will be re-created from scratch when the application is restarted.

Don’t change any table schemas or add any tables in the database directly. We want to manage our schema and seed data from within the Hasura console because it will automatically create migration files for us. On application start up any new migration files will be applied to the database.

That being said, let’s head on over to the Hasura console at localhost:9695

You should be greeted by the GraphiQL explorer where you can make some test queries, mutations etc. Let’s add another table to see how the migrations work before moving on.

Click on the Data tab at the top, and click the yellow create table button beside “Schema”.

From here, we’ll add a users table with a few columns and set the Id column as primary key before clicking the “Add Table” button at the bottom of the page.

Once the table is added, you’ll notice in your hasura/migrations folder that 2 migration files were created. Hasura will migrate your local database instantly when you make changes but any remote database will have the latest migrations files applied on next application start up.

We can now add a user into our new table with the GraphiQL explorer on the main page of the console.

This should be enough to start poking around with GraphQL in the demo app. You can add tables from within the console and start customizing the GraphQL queries in the application to see how it all works together. There’s plenty of amazing articles at blog.hasura.io that go into great depth regarding just about anything you’d want to know.

Authentication is beyond the scope of this post but you can check out some of the links in the hasura/notes.md file for more info and some excellent examples.

Cool… How do I deploy it?

Set up the Google Cloud SDK

Install Google Cloud SDK if you don’t already have it, instructions can be found here https://cloud.google.com/sdk/docs/quickstarts

Open a terminal window to enter the following command line instructions.

Make sure to update cloud components if you’re using a pre-installed version

gcloud components install beta
gcloud components update

Authenticate your account

gcloud auth login

Set some sensible defaults (Im in Toronto so I’ll be using us-east)

gcloud config set compute/region us-east1
gcloud config set compute/zone us-east1-b
gcloud config set run/platform managed
gcloud config set run/region us-east1

Create a new project by running the following command and answering Yes to the prompt.

gcloud projects create \
--name hasura-test-app \
--set-as-default

Set up a billing account

We will have to enable billing for the newly created project in the Google console admin panel here https://console.cloud.google.com/billing. Click on the “My projects” tab then on hasura-test-app select the “actions” drop down and choose “change billing” and set a billing account.

Create and configure a database

Now that we’re set up with the gcloud command line, we need a database server instance to support our GraphQL backend.

Enable the SqlAdmin API by running the following command

gcloud services enable sqladmin.googleapis.com

Create a new CloudSql server by running the following command — this can take a couple minutes

gcloud sql instances create test-app-db \
--availability-type zonal \
--no-backup \
--database-version POSTGRES_9_6 \
--root-password {secure_password} \
--storage-type HDD \
--tier db-f1-micro \
--zone us-east1-b

Create a fresh database in your new CloudSql instance

gcloud sql databases create hasura --instance test-app-db

For brevity I will configure the default user (postgres) with a new password. Please make note of the password you choose and be sure to replace SECURE_DB_PASS with it throughout the walkthrough, if you don’t care about the security for now, you can copy and paste everything verbatim.

gcloud sql users set-password postgres \
--instance test-app-db \
--password SECURE_DB_PASS

Run the following command to create the environment variable TEST_APP_DB.

TEST_APP_DB=$(gcloud sql instances describe test-app-db | \
grep connectionName | \
awk '{print $2}')

Verify that TEST_APP_DB is set correctly by doing the following command:

echo $TEST_APP_DB

You should see output similar to:

hasura-test-app-252115:us-east1:test-app-db

We will also set an environment variable for the project name. Run the following:

export TEST_APP_PROJECT_ID=$(gcloud config list --format 'value(core.project)')

Verify that TEST_APP_PROJECT_ID is set correctly by doing the following command:

echo $TEST_APP_PROJECT_ID

You should see output similar to:

hasura-test-app-252115

Deploy the GraphQL server

Before we deploy our Hasura application and connect it to our new database we need to grant our application access to use the database by setting IAM permissions on the CloudRun service account. We also need to enable Google CloudRun and CloudBuild.

Enable the CloudBuild service by running

gcloud services enable cloudbuild.googleapis.com

Enable the CloudRun API at https://console.cloud.google.com/run

** Make sure you have the hasura-test-app project selected in the blue dropdown at the top

Click on “START USING CLOUD RUN”

To allow CloudRun to access to our new database follow this link https://console.cloud.google.com/iam-admin (make sure you have the hasura-test-app project selected) and click the edit button beside the member that ends in @serverless-robot-prod.iam.gserviceaccount.com

In the editing panel:

  • Click ADD ANOTHER ROLE
  • Type “sql” in the filter box
  • Select Cloud SQL Client
  • Select save

Now we’re ready to run the following command which will build a docker image of the Hasura GraphQL server and push it up to the Google registry

  • Answer YES if you are prompted to enable any additional api’s. This prompt may show if it’s the first time you’ve used the gcloud builds command.
  • If the command fails the first time (even after accepting), don’t worry — wait a minute and run it again. Sometimes the permission changes take a second to apply.
gcloud builds submit ./hasura \
--tag gcr.io/$TEST_APP_PROJECT_ID/graphql-server:latest

Once the previous command has completed and we’ve successfully pushed up the docker image, run the following to deploy the application on CloudRun

  • Hit enter when prompted for a service name to accept the default
gcloud beta run deploy \
--image gcr.io/$TEST_APP_PROJECT_ID/graphql-server:latest \
--region us-east1 \
--platform managed \
--set-env-vars HASURA_GRAPHQL_DATABASE_URL="postgres://postgres:SECURE_DB_PASS@127.0.0.1:5432/hasura",CLOUDSQL_INSTANCE=${TEST_APP_DB} \
--timeout 900 \
--allow-unauthenticated

We can run the following curl command to test that everything is up and running, replace {CLOUDRUN_URL} with the url in the console output.

  • Note: The first time you hit the endpoint it will wake the application to run the migrations and the CURL command will return an error. As long as it returns at all its a good sign, the database will be migrated shortly after. Wait a minute or two before hitting it again and you should see the same output as the image below.
curl -X POST {CLOUDRUN_URL}/v1/graphql --data-binary \
'{"query":" {test { id firstname lastname }}","variables":null}'

The following output means the GraphQL server has been successfully deployed !

Deploy the Next.js application

Duplicate the .env.dev file and name it .env, this will be the environment variable file for the production build.

Modify the GRAPHQL_API_ENDPOINT variable in your new .env file to your own url as follows {CLOUDRUN_URL}/v1/graphql and set both NODE_ENV and MODE to “production”

Once again, we’ll run the following 2 commands to first build the docker image then deploy it to CloudRun. Once the second command has been run, you should be able to navigate to the url and see the Next.js app live!

# Build the docker image
gcloud builds submit ./app \
--tag gcr.io/$TEST_APP_PROJECT_ID/next-app:latest
# Deploy to CloudRun
gcloud beta run deploy \
--image gcr.io/$TEST_APP_PROJECT_ID/next-app:latest \
--region us-east1 \
--platform managed \
--allow-unauthenticated

To redeploy the applications, you just need to re-run the gcloud builds submit and gcloud beta run commands for whichever application you want to re-deploy. If you make Hasura changes/migrations on your local development environment, redeploying will apply them to the CloudSql database.

Congratulations! You’ve successfully deployed a serverless Next.js app and GraphQL backend on top of a CloudSql database.

Bonus: Add a custom domain

If you want to point a domain name to your app, go to https://console.cloud.google.com/run/domains and select “ADD MAPPING”. Once you’ve chosen which app you want to add a domain for, you’ll be instructed on how to verify the domain and update records for your chosen provider. Google will automatically configure SSL for custom domains.

That’s all folks!

At Tactable we’re super excited about the future of serverless technology and the amazing stuff we’ll be able to build with it. Please contribute any questions or insights in the comments!

Big thanks to the creators and contributors of nextjs-redux-starter which I forked the next app from, It’s one of the nicest base templates I’ve personally come across 🍻

Ps. We’re located downtown Toronto and looking to hire great people!

careers@tactable.io

--

--