Building a Music Playlist App with Gatsby, Contentful and Hasura Remote Joins
Tags: gatsby, contentful, auth0, remotejoins
Published: 2019-12-12 09:31:24

---
title: Building a Music Playlist App with Gatsby, Contentful and Hasura Remote Joins
published: true
date: 2019-07-26 09:13:11 UTC
tags: gatsbyjs, contentful, auth0, remote joins
canonical_url: https://blog.hasura.io/building-a-music-playlist-app-with-gatsby-contentful-auth0-hasura/
---

## TL;DR

Join music data in your database with audio source files in Contentful with GraphQL and Hasura Remote Joins. Handle authentication using Auth0.

Instant setup. App Source Code 👉  [gatsby-contentful-auth0](https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/gatsby-contentful-auth0)

![](https://blog.hasura.io/content/images/2019/07/gatsby-contentful-remote-join-4.png)

In this example, we will look at how a music playlist app can be built with Gatsby and CMS as Contentful. [Contentful](https://contentful.com) is an API First CMS to build digital products. It offers a [GraphQL API](https://www.contentful.com/developers/docs/references/graphql/#/introduction/basic-api-information) which can be joined with Hasura using Remote Schema. We will learn how these multiple data sources can be merged with existing data in Hasura. In the music playlist app, we store metadata about music tracks, albums, playlist and users in Postgres database and query it using Hasura GraphQL. We will use Contentful to store and retrieve audio tracks required to play music on the app. For user authentication we will make use of Auth0.

Here's a simple schema representation of the music playlist app that we are going to build. There's an `album` table which can have multiple tracks. Similarly there's a `playlist` table which can have multiple tracks. A playlist is linked to a user via the `user` table.

![](https://blog.hasura.io/content/images/2019/07/playlist-app-schema.png)

The track information is stored in Postgres but we use Contentful to store and manage actual audio tracks.

### Getting Started

This post is a part of our [Remote Joins](https://blog.hasura.io/remote-joins-a-graphql-api-to-join-database-and-other-data-sources/) (available in preview) series. Remote Joins in Hasura allows you to join data across tables and remote data sources. You can sign up [here](https://hasura.io/remote-joins) if you'd like to be notified when we launch. Head to [the PR](https://github.com/hasura/graphql-engine/pull/2395) to check out more details, read the preview docs, and to try out a test Hasura image that you can spin up. Jump on our [discord](https://discord.gg/hasura) or comment on [github](https://github.com/hasura/graphql-engine) and let us know what you think!

Now let's setup Auth0 for the Authentication part. Refer to the [Auth0 integration guide](https://docs.hasura.io/1.0/graphql/manual/guides/integrations/auth0-jwt.html) for the configuration.

In the sample app that you cloned above, you need to apply the rule which syncs user signups to the postgres database. So everytime a user signs up on the app, the user data is inserted into the database.

### Storing Media Files in Contentful

Let's create necessary content types in Contentful. Under **Content model** , create a new content type for **Tracks** and add the following fields -\> `id` and `track`. We will use the `id` field to connect this data with Hasura track table. Note that track field is of Media type and we will make use of it to create audio source files.

![](https://blog.hasura.io/content/images/2019/07/contentful-media.png)

### Adding Contentful as Remote Schema

To be able to query Contentful data via Hasura, it needs to be added as a **Remote Schema** using the Hasura Console.

- Get the GraphQL Content API Endpoint in the following format:

```
https://graphql.contentful.com/content/v1/spaces/<space-id>
```

And replace with the appropriate space id.

- In Contentful dashboard, click on **Settings**. Under **Space Settings** click on **API keys**. Copy the Space ID and paste in the above endpoint.
- Now copy Content Delivery API - access token and use it in Authorization headers like below:

```
Authorization: Bearer <access_token>
```

- In Hasura Console, head to `Remote Schemas` and enter GraphQL Server URL with the above contentful endpoint. Under Additional Headers, enter the Authorization header with the access\_token as mentioned above.

![](https://blog.hasura.io/content/images/2019/07/add-contentful-remote-schema-1.png)

Create Remote Relationship from `playlist_track` table to `tracksCollection` type of Contentful.

![](https://blog.hasura.io/content/images/2019/07/remote-relationship-track.png)

Under configuration for tracksCollection, we declare a where clause to say:

```
id: From Column -> track_id
```

This ensures that while querying for tracks, we get only the relevant audio source files.

Now the GraphQL query to fetch this data in a single API call would look like the following:

```
query PlaylistQuery {
      playlist {
        name
        tracks {
          track_details {
            name
          }
          track {
            items {
              track {
                url
              }
            }
          }
        }
      }
  }
```

Note that the nested query `track` comes from Contentful and it will apply the filter of track.id = playlist\_track.track\_id, there by giving only the relevant audio track information.

### Authorization

We have merged two different data sources and retrieved data in a single GraphQL query. With role based permissions, we can take care of the permissions required for the app. For example, a user should be able to add tracks only to the playlist that they created.

Insert some sample data to see how the response structure looks like for the above GraphQL query.

![](https://blog.hasura.io/content/images/2019/07/graphiql-response-contentful.png)

The track URL information comes from Contentful and more metadata can be fetched about that track. Using the URL, we build the playlist app showing the tracks belonging to the user's playlist.