How to Build GraphQL Server For the App Like Google Play Music

Let’s assume that you’ve read our previous article about what GraphQL is in ‘ Building Scalable Enterprise Products with GraphQL ’ and have a theoretical understanding of the concept behind GraphQL . Now it's about a time to see this technology in action. In this small GraphQL server tutorial we will take a look how to build a server-side app like google play music, using GraphQL with MongoDB/NodeJs/Express.

GraphQL server setup

What is the first thing you are trying to do when you start learning a new language? Yeah, let's start our GraphQL server tutorial by saying hello to the world with GraphQL:

And root.graphql

Several key points to highlight form the examples provided above:

  • You can add GraphQL to your project as a regular express middleware often on ‘/public’ or `/graphql` endpoint.

  • GraphQL requires request to be in json format. So we need bodyParser.json() to build a music streaming app that would work correctly

  • We are using .graphql files for our type definitions. The main benefit of this approach is clear syntax which is a really valuable for big GraphQL schema example. If you don’t use any tools like webpack or Babel, you can read files with fs module, like we did in this GraphQL server tutorial.

Now, run the project and open the following address in the browser: http://localhost:3000/graphiql. You will get to GraphiQL UI tool. On the left sidebar type { hello } and you will see the following:

how-to-build-graphql-server-for-the-app-like-google-play-music

Type Definition

Now we are ready to declare our schemas. Let’s take a look at our Track type.

After we are done with our GraphQL types for tracks and artists, the next step is to create a special top-level type, called Query by convention:

Mongoose models

Before we move to the implementation of resolvers, let's make a quick overview of Mongoose modal definition. Note that the schema of your DB collection isn’t related to any relevant type definition, but in most cases it will have a similar structure.

GraphQL resolvers example

So how does GraphQL resolver look like? Well, it's a plain JavaScript function that takes four arguments: the previous object, an object of passed arguments, context and info about a request. The resolver may be synchronous or return a promise. Actually, Mongoose queries aren't promises but we can use them in the same manner (learn about the difference between queries and promises from Mongoose's docs) so our resolvers for root queries can be quite straightforward requests to DB

It's not a complete example yet. Artists query should return [Artist!] with tracks and albums fields which are not stored in artists collection. Basically, if some fields from GraphQL type aren’t in the returned object, or are modified, we should implement additional resolvers for such fields. Here are the resolvers for Artist type:

As you can see, their structure is look-alike, except for artistType which is a little bit confusing. Let’s figure out why:

  • artistType has an abstract interface type and GraphQL must resolve some particular type, like Band or Single. That’s why, in this GraphQL server tutorial, we recommend you to create special __resolveType which helps GraphQL to make the right choice.

  • Since BandParticipant should enable us to find the Band by its current member, we need to modify artistType object to drill it to down to current artists _id.

Data loaders

What about the performance of the app, powered by GraphQL server? Let’s take a look at the example with Albums endpoint. Imagine that we are trying to fetch 50 documents in a single request and our query is equal to:

how-to-build-graphql-server-for-the-app-like-google-play-music-2

In this GraphQL server tutorial, we will fetch 50 album docs in a single request to DB first, which is totally ok.

But after that, we get executed resolvers for fields like artists, tracks and title Track for each album that will also produce separate requests to DB. So the total number of requests to DB just for one GraphQL query to the Albums endpoint might be 1 + 50 * 3 and obviously lead to slow execution.

Fortunately, this problem can be easily resolved using Data Loaders (read more about DataLoaders on Facebook’s Github page). The main idea of dataloader is batching our requests in a single tick of event loop. DataLoader will gather all the requests and execute them once instead of immediately executed requests sent to the DB. Here is the example of artists and tracks loaders for our app like google play music.

Now we should add instances of dataloaders to GraphQL middleware’s context option. It’s important to create new instances of data loaders per each request to avoid issues related to caching old data from previous requests.

Once we are done with the previous step, we can access created dataloaders from context (third argument) in resolvers and replace Mongoose find/findById with loadMany/load.


As a result, we will make only 3 requests to DB: the first one stands for Albums, the second is for Artists, and the third one is for all Tracks.

Mutations

We were talking about fetching data with queries till this time. Now, to continue to build GraphQL server, we have to make some changes in DB, so we should use mutations. They mostly look the same as queries do, but have their own differences:

  • Request to GraphQL starts with keyword `mutation`

  • All mutation endpoints should be under Mutation type.

  • For mutations used POST method instead of GET method.

  • All mutations run in a queue, as opposed to queries which can be run in parallel.

Very often mutations can take a large list of arguments which makes your query definition difficult to read. For this reason, there’s a special Input object type that can be passed as argument in GraphQL schema language.

Here is an example of what a mutation might look like:.

Input type provides some top-level validation so we can make sure that received input has `releasedAt` field which is not-nullable and has Date type. That means that we can avoid some additional server-side validation like if (!input.releasedAt), etc. because GraphQL does it by itself.

The example below shows the result of mutation calling to add new track to DB. As you can see, we’re also returning created track immediately, and can use subselection in mutation like in regular query. Pay attention that in previous examples, when we were passing values directly to the query, we used something called static params. But most of queries in real-life application will take arguments via variables.

how-to-build-graphql-server-for-the-app-like-google-play-music-3

Summary

As we can see from this GraphQL server tutorial, building a GraphQL backend for the app like Google Play Music project is not so hard. In conclusion, I’d like to add that GraphQL has a several advantages over regular REST APIs:

  • More efficient way to fetch data since we can use multiple queries in on request to server.

  • Predictable shape of data returned from server;

  • With sub-selections it’s possible to fetch specific fields only if needed.

  • Powerful data validations on field level

  • An easy way to resolve complex relation structure between DB collections.

  • GraphiQL provides cool tools to test your API without writing a single line of UI code

  • Strong JS community with a lot of useful packages

We hope that you’ll find this article helpful, whatever kind of the project you’re dealing with, whether it’s an online marketplace platform or a music streaming app.