Building a serverless Nuxt app using GraphQL and Hasura Remote Joins
Tags: remotejoins, graphql, tutorial
Published: 2019-12-16 13:22:34

---
title: Building a serverless Nuxt app using GraphQL and Hasura Remote Joins
published: true
date: 2019-09-17 11:28:41 UTC
tags: remote joins, graphql, tutorial
canonical_url: https://blog.hasura.io/building-serverless-nuxt-app-graphql-hasura-remote-joins/
---

![](https://blog.hasura.io/content/images/2019/09/hasura-azure-w_o-border.png)

_This tutorial was written by [Travis Reynolds](https://travisreynolds.dev/) and published as part of  the [Hasura Technical Writer Program](https://blog.hasura.io/the-hasura-technical-writer-program/) - an initiative that supports authors who write guides and tutorials for the open source Hasura GraphQL Engine._

In this guide, we will build a simple Instagram-style Nuxt app, where users can upload images, like and also add comments to other people's images. Images will be stored on an API, and we will use Hasura Remote Joins to transform image URLs so we can query the exact-sized images we want from the database, instead of having to transform the URL client side with a dedicated library.

**Remote Joins** is an upcoming Hasura feature that allow you to join external services across your database. Check out the official blog post [here](https://blog.hasura.io/remote-joins-a-graphql-api-to-join-database-and-other-data-sources/).

## Prerequisites

We will be using a pre-built Nuxt app as the frontend, so some experience with VueJS and SSR frameworks will be helpful - although not necessary.

For the backend, we will use:

- Hasura for GraphQL APIs & Remote Joins
- Heroku
- Azure Functions to create a remote schema and events handler, 
- Auth0 for authentication
- Cloudinary to host our images. 

Let's get started!

> _You can view the demo app here:_ [https://uncover-nuxt.herokuapp.com/](https://uncover-nuxt.herokuapp.com/)

Firstly, download the [sample repository](https://github.com/thetre97/uncover). In this, we have a couple of folders:

- Migrations - We will use these to setup the tables for our database
- Functions - The Function app we will deploy to Azure
- Uncover - The Nuxt frontend app

## Setting up the Database 

We will be using Heroku to deploy a Hasura instance. There is already a nice [**quick start guide**](https://docs.hasura.io/1.0/graphql/manual/deployment/heroku/index.html) on deploying to Heroku - Follow this, but we need to change one thing. Currently, the Remote Joins feature is still in preview, so we will need to use the Docker image created from the Github PR. Visit the [PR](https://github.com/hasura/graphql-engine/pull/2395) and find the latest build by the hasura-bot. It will look something like `hasura/graphql-engine:pull2395-<version>`. As of this writing, it is `hasura/graphql-engine:pull2395-973f1496`. Make a note of this, then edit the Dockerfile `FROM` value to the PR version.

```
FROM hasura/graphql-engine:pull2395-973f1496
```

We can now deploy this to **Heroku** - follow the rest of the **quick start guide**.

Once Hasura is deployed, follow this next article on [**securing the Hasura console**](https://docs.hasura.io/1.0/graphql/manual/deployment/heroku/securing-graphql-endpoint.html).

Nice work! We can now visit our app at `<app-name>.herokuapp.com/console`. My app is called **Uncover App**, so I can reach the console at [**https://uncover-app.herokuapp.com/console**](https://uncover-app.herokuapp.com/console). Go ahead and login with your admin secret (from the quick start guide above).

![](https://lh3.googleusercontent.com/T3nML9BPg4W2-Df8QzaVyi3cc8jpVrpuJZ2Kurimt56gmQ9Nki1Wm__PN-WaSpePXKBQN4SiHuOU3K8w4cB2-DZUyuO3pyHYla9TCbZwmPzRhw509C18AFFRD7xXb3sSQ2rUj4nE)<figcaption>The Hasura console</figcaption>

We need to add one more environment variable to Heroku: this will decide what role any unauthorized users will be given - so we can add permissions accordingly:

```
HASURA_GRAPHQL_UNAUTHORIZED_ROLE = public
```

Now we need to apply the migrations to Hasura. As we are using the PR docker image, we will need to download the Hasura CLI for that version. Follow the links below to download the CLI for your platform:

- [Mac](https://51302-137724480-gh.circle-artifacts.com/0/cli/binaries/cli-hasura-darwin-amd64)
- [Linux](https://51302-137724480-gh.circle-artifacts.com/0/cli/binaries/cli-hasura-linux-amd64)
- [Windows](https://51302-137724480-gh.circle-artifacts.com/0/cli/binaries/cli-hasura-windows-amd64.exe)

I am using Linux, and I have downloaded the CLI to my uncover folder.

Now we can apply these migrations to Hasura - simply run the following command, replacing your endpoint and admin secret:

```
./cli-hasura-linux-amd64 migrate apply --endpoint <your-endpoint> --admin-secret <your-secret>
```

This command will take a few seconds to run, and once it is done, you should be able to see all the tables needed for our app in the Hasura console.

![](https://lh6.googleusercontent.com/DinrwIb41bpkpCmGPsQQE5CtptpLxqMFDXx9GhJmSW1qIE7_RAlnAdLyNKNxq8i3XetRtuvQmb0MFiF4NaNTwb3cxOME4HNeynSYscnQXObIP33jrRoPwqetxGIyWxindZTBy_bP)

## Setting Up Auth0

Next we need to setup Auth0 to manage authentication - refer to the [official guide](https://docs.hasura.io/1.0/graphql/manual/guides/integrations/auth0-jwt.html) to configure this. Add[](http://localhost:3000/login/callback)`http://localhost:3000/login/callback` as the Allowed Callback URLs and Allowed Web Origins.

Make sure to note the created application ID and Domain - we will need these later.

![](https://lh4.googleusercontent.com/rZVzoPetZpOlGTIGa77KSeduDyHY6ly9DZ7Wka0slJyVn7cRl-ORwWdo_2g20WamYOL7w-ITvX8mtXE0-SF7ZoeP_oTiDHbd3m_kBeotcv2PUmK_ZnGkPE0Hug6nox9b-Vk55M5Z)

## Cloudinary

We will be using Cloudinary to host our images, and to run transformations on them. Visit [their website](https://cloudinary.com) to create an account. Once you are logged in, make a note of your cloud name.

![](https://lh6.googleusercontent.com/kfeEx46YdJtroCz79Z4KW5fhT-1Zzm3ZJZf_QhmPXU7Q9D6fUzzCZzCgEK0meu8wLkxVq4yGwU-Ue-ySt6XtvLfQDAR3YndhezkROitBH4E0QBF77WmRm6X1zWlGtGHiy1H1eUYz)

We will need to create an upload preset to allow unsigned uploads from our app, so click on the settings icon in the navbar. Now select the Upload tab, and scroll down to the Upload presets: tab. Click Enable unsigned uploading, and make a note of the name of the preset that has just been created - it's mode will be Unsigned.

![](https://lh4.googleusercontent.com/rpP77_VR-ESa8cKVauIgkamZIRnluvgy9f0DXwYzEjnoSMDbzxu-XH9g8s_xKnz3xGUKfkdFPvMn8rPXo6GmybsJ6QZ4d8LLXLYqxcpzATozbkCiHoPCCFiQ9eak1MsRlR7pVmgG)

We can now start setting up our functions app, which will run our 'remote' GraphQL server.

I will be using **Azure + VSCode** for this, however you can use **Zeit Now**, **AWS Lambda**, etc.

## Azure Setup

If you already have an active account and subscription with Azure, you can skip these next steps.

Visit [https://azure.microsoft.com/en-gb/free](https://azure.microsoft.com/en-gb/free), and click **Start for free**. Azure gives you a number of services for free with an active subscription, including 1 million function executions a month - which will be more than enough for our demo app.

You can either create a new Microsoft account, or login to an existing one. I will create a new account for this. Just follow the few steps to add an email address, password, and email verification.

At the next step, you will continue to setup your free account. Fill out the necessary details, then you will need to add a phone number, and a card for verification -

> Don't worry, you won't be charged at all for this demo app. _You won’t be charged unless you upgrade._

Agree to the T&C's (after carefully reading them through, of course...) and click **Sign up**. On the next screen, Click **Go to the Portal** , and you should be greeted with a screen like this:

![](https://lh6.googleusercontent.com/ABl_OEVkRhLgRD05kmfqwcKFxEW5IifYHLUaCK53EFDDMyci_Bjeg8JzWlSJQCSq4e3SAso6tR-GGUDZZ70TROpyjVBItTzvdClIYu-xg8yOFUOOmw2EaeZ4IISe2GaPLTApy3mp)

We can leave this for now, we will be managing our Function app from VS Code.

## Functions CLI

To run our function app locally, we will need to install the 'Core Tools' packages. Instruction for this can be found [here](https://docs.microsoft.com/en-gb/azure/azure-functions/functions-run-local).

Once installed, open Code, and head to the extensions page. Search for **Azure Functions** `ms-azuretools.vscode-azurefunctions` and install this package. You will notice that another extension that this package requires is also installed, Azure Account.

![](https://lh5.googleusercontent.com/0oIF0VoD4lSzFWwaiaYWnhQtP8dqEoL2Zle6yw-bSq8JeqB1AKZYOZx47qUOq3PfYW1eHrP_j-lqvECU_cudMdHP2FSGgnQL33k8HfP_uYtW0X4-XNtztXmQjA4Tha4mRSdIZFLr)

Once these have finished installing, press F1, and type **Sign In**. You should see an option called **Azure: Sign In** - navigate to this, and press enter. A browser window will open, asking you to authenticate with an Azure account. Choose the account we have just setup, and you will be redirected to a page confirming the sign in.

![](https://lh4.googleusercontent.com/smmX0iKH-oMvRFkg57V-0ooWn0DnfM4V5lkUxLyNpGNZXqI9XHyq7l7dtIMt8gfxQ_a9NrNw8tEczCL6aQjTctFCXMX17UhbcrgXJONSeo7GxhL878LdNN3bQ-VisqwSbdxi1h2o)

Great, we are all set up! Now we can deploy our Function app.

Open Code in a folder which will hold the function, and select the Azure icon in the left hand menu.

![](https://lh5.googleusercontent.com/ldt56I4iwCRtCK4mARZEqyje6sNiZgr-M4Rw6ZG9IVI_gBvWUqVfs94Od3bIA8Sp31QziZWJ8uoUmv0e3csBoR-K3fbmnkGuKg1LmAPrfXqf4vYnudwbIjiR_r80uq5kVnMVVB6s)

As yet, we haven't added any subscriptions for the extension to use. Click **Select Subscriptions**, and choose a subscription to show. Mine was called **Free Trial**, so I selected that, and clicked **OK**.

You will see your subscription shown in the side panel now. Right click on that, and select **Create Function app in Azure**. In the popup, name your Function app (note this will also be part of your URL), and select a location. Hit enter, and the extension will set up a complete new Function app for you, configuring all the resource groups/storage accounts etc necessary. You will have to wait a few minutes for the setup to complete, but when it is done, you can deploy your app.

_If you want to configure this manually, you can select the Create Function app in Azure (Advanced) create option._

As the functions are built with Webpack, we need to run a build command before deploying. But before that, we need to set some ENVs. Create a new file called `local.settings.json`, and paste the following contents into it:

```
{
  "IsEncrypted": false,
  "Values": {
    "CLOUDINARY_NAME": "<unsigned upload preset name>"
  }
}
```

Open a terminal, **cd** into our functions folder, and run the below commands:

```
cd functions
yarn # Or npm i
yarn build # Or npm run build
```

Now you will see an index.js in our root graphql folder.

Open the Azure sidebar again, and select the blue upload arrow in the Functions section. Once you have clicked this, a popup will appear, asking what folder we want to deploy. Select the functions folder, then select the Functions app you have just created.

Click **Deploy** in the next confirmation box, and our app will start to deploy our functions.

Once this process has finished, a notification will appear confirming a successful deploy. Make sure to click on **View Output** , and copy down the Http Trigger URL that is shown - this is the URL to our GraphQL server. Next, we need to upload our local settings (ENVs) to our Function app. Tap F1, and type in **Azure Functions: Upload LocalSettings**. Hit enter, and make sure to choose the `local.settings.json` from our functions folder. Select our function app, and press enter. Now the extension will add the local settings to the remote Function app.

## Remote Join

Visit your **Hasura Console** , and click on the **Remote Schemas** tab. Click **Add** , and type in a name for your remote schema - **GraphQL** will do. Then paste your Function app URL ([https://\<app-name\>.azurewebsites.net/graphql](https://uncover.azurewebsites.net/graphql)) into the URL input. Now we can click **Add Remote Schema** , and we have joined our two GraphQL schemas!

![](https://lh6.googleusercontent.com/UK6Y5EanMqycCHXEIZVH7RLwbqfiRJ5a75UKdjFGCqUQWT8tMsNTT2puuGXGRxOA-j1Bm-MuwGPtVzuk1sDpJ4BsZG6NgN2NoO2rK5y7GT161h8khjWh_OYiYc0CRxDJeUpacgLD)

Next, click on the data tab. Browse to the images table, and select the **Relationships** tab. At the bottom of the page, there is a section called **Remote Relationships**. Here comes the fun part...

Click Add, name the relationship `transform_url`, and select our GraphQL remote schema.

Now we have to configure it. We want to use the `transform_image_url` resolver, so select that. Now we need to pass it our url to transform. To do so, find the `url` argument, and select it. Make sure the columns that appear are set to `From Column` and `url` - then click save.

![](https://lh6.googleusercontent.com/MAcOpJfLmItDvB_AlpY-_VeZ1yjHJJIrZ8LGgbftfCDWclTBr22NvL8Le66mqysEznO1Y1hEaHdqii61AXk_Dtz0Aro2cH4eXwldwYh859FnzBWDcnmTuWXw6mr8al5aph9DkJFn)

Here, we are telling Hasura that we want to use the `url` column from our images table as an argument for `url` on the remote schema.

## Nuxt Frontend

Next step is to run our Nuxt app.

Open the uncover directory in VS Code, and add a new `.env` file at the root of the project.

You will need to add the below values:

```
APP_URL=http://localhost:3000
```
 
```
AUTH0_CLIENT_DOMAIN=<auth0-application-domain>
AUTH0_CLIENT_ID=<auth0-application-id>
APOLLO_ENDPOINT=<hasura-endpoint (https://<app-name>.herokuapp.com/v1/graphql)>
APOLLO_WSS_ENDPOINT=<hasura-wss-endpoint (wss://<app-name>.herokuapp.com/v1/graphql)>
CLOUDINARY_PRESET=<unsigned-upload-preset>
CLOUDINARY_NAME=<account-name>
```

Now we can run the app!

```
cd uncover
yarn # or npm i
yarn develop # or npm run develop
```

You can view the app on [http://localhost:3000](http://localhost:3000).

![](https://lh6.googleusercontent.com/iS-5Tqhgm0NnJp6YGeNaF9Z0bdNe7ukWXy-Pt6nCpSriQZ_X-W1uJ56_WACpaLAzXtthpqVGdjjMPwiUORJJgwpO0TLoGlg43BcNQpC6UVxVzKuTc0HrSJHGRSbC-62QX0R7AMuP)

The GraphQL query that is used to get data from the remote join looks something like this:

```
query AllImages {
  images {
    id
    created_at
    title
 	description
    altText
    placeholder: transform_url (width: 300, height: 200, quality: 20, gravity: "center", crop: "fill") # Remote Join Field
    card: transform_url (width: 600, height: 400, gravity: "center", crop: "fill") # Remote Join Field
    full: transform_url (width: 1200, height: 800, gravity: "center", crop: "fill") # Remote Join Field
    user {
      profile {
        username
        last_name
        first_name
      }
    }
  }
}
```

We have a few different queries here. Firstly, we are querying for all images in the database. Then, we fetch data that we will use in our app - for example, the title, description, the user it belongs to, and their profile. Then we query the `transform_url` field multiple times.

Firstly, we query it to get a placeholder image url - this is a small and low quality image that can be loaded quickly. It will be shown first as the higher quality image loads (For example, like Medium.com images). Then we query for the card image - this is a higher quality image that will be shown in the timeline. Finally, we have the full image. This will be used in the lightbox when a user clicks on an image.

## Summary

Remote Joins is very useful here, as we can query it multiple times under different aliases to get different transformed urls, using a serverless function to do the heavy-lifting. If we did not use a remote join, we would have firstly had to install the Image CDN specific client library, which are often very large - for instance the `vue-cloudinary` package will add ~22kb to our Nuxt app. Then when we had queried the image url, we would need to go through each image object, and use the client library to produce the transform\_urls each time we view an image - this can take valuable load time.

Instead, we immediately have all the information needed to start showing images.

## About the author

Travis Reynolds is a freelance frontend engineer from Portsmouth, UK, but he often tinkers around with NodeJS and GraphQL as well - he likes to learn new things that may help him in future projects. You can check out his infrequently-updated blog at [travisreynolds.dev](https://travisreynolds.dev).