Defining GraphQL queries and generating code using Apollo

As we will soon see, the code generation feature of Apollo GraphQL is one of its big advantages. Thanks to this, we will be able to ensure that the queries we write on the client-side are valid. They will be checked against the server-side GraphQL schema. Also, TypeScript interfaces will be generated for the queries and their parameters, allowing us to write type-safe code while interacting with the backend GraphQL API.

We will make our code evolve one step at a time:

  1. First, we're going to prepare our GraphQL queries.
  2. Then, we will use the Apollo CLI to generate TypeScript code.
  1. After that, we will create an Apollo Client inside of the Home page.
  2. Finally, we will use the client to search for artists and songs when the search component calls our search handler function.

Let's get to it. First of all, do the following:

  1. Create a new folder called graphql under frontend/src.
  2. Create a queries.ts file under frontend/src/graphql.

Then, add the following contents to the queries.ts file:

import gql from 'graphql-tag'; 
 
export const FindArtistsQuery = gql` 
  query FindArtists($value: String!) { 
    artists(name: $value) { 
      id, 
      name 
    } 
  } 
`; 
 
export const FindSongsQuery =  gql` 
  query FindSongs($value: String!) { 
    songs(value: $value) { 
      id, 
      name, 
      hasLyrics 
    } 
  } 
`; 
 
export const FindLyricsQuery = gql` 
  query FindLyrics($id: String!) { 
    songLyrics(id: $id) { 
      id, 
      lyrics, 
      explicit, 
      copyright 
    } 
  } 
`; 

These are the only three queries that we will use. For all of these, a String parameter is made mandatory with the ! character. For the FindArtistsQuery and FindSongs queries, this parameter respectively corresponds to the artist and song name to look for. For the FindLyrics query, it is the identifier of the song to retrieve the lyrics for.

Each query also specifies the fields it wants to retrieve. In the case of FindSongsQuery, we ask for the hasLyrics flag to know whether there are lyrics available. We will use that information, later on, to filter out the songs for which no lyrics are available.

At this point, we don't know for sure whether our queries are valid as these are just plain strings. Let's fix that with the help of Apollo!

Open the frontend/package.json file and add the following scripts to it:

"apollo:download": "apollo schema:download --endpoint=http://localhost:4000/graphql graphql-schema.json", 
"apollo:generate": "apollo codegen:generate --includes=src/graphql/queries.ts --endpoint http://localhost:4000/graphql --addTypename --target typescript --globalTypesFile build/ignoreMe.txt --tagName gql --outputFlat src/generated --customScalarsPrefix lyricsFinder" 

The first script, apollo:download, is a small utility that will download the GraphQL schema in JSON form from the backend server. You can run this script if you're curious about what the final schema looks like. We won't be using this file in our project though.

In a real project, you might want to keep a copy of the schema in your client-side project (assuming the backend API is correctly versioned) to avoid being dependent on the backend server's availability for building your frontend application.

The second script, apollo:generate, uses the codegen:generate command of the Apollo CLI. As you can guess, its goal is to generate code for our queries, based on the GraphQL schema.

Here's an explanation of the different parameters that we make use of in the apollo:generate script:

You can find the complete CLI reference here: https://github.com/apollographql/apollo-tooling#apollo-clientcodegen-output.

Generally speaking, the generated code should be kept out of your source code repository. In this case, you could add the frontend/src/generated folder to the .gitignore file.

Now that you have added the code generation script, you can execute it:

yarn apollo:generate

Once done, you should find the generated code under frontend/src/generated.

Let's take a look at the FindArtists.ts file:

/* tslint:disable */ 
/* eslint-disable */ 
// This file was automatically generated and should not be edited. 
 
// ==================================================== 
// GraphQL query operation: FindArtists 
// ==================================================== 
 
export interface FindArtists_artists { 
  __typename: "ArtistDto"; 
  id: string; 
  name: string; 
} 
 
export interface FindArtists { 
  artists: FindArtists_artists[]; 
} 
 
export interface FindArtistsVariables { 
  value: string; 
} 

Inside of it, you'll find three different interfaces, each serving a specific purpose:

If you look at the other generated files, you'll see that they have the same base structure.

Of course, this example is trivial, but it clearly shows that we can easily generate TypeScript code based on GraphQL queries and validated against a schema. We will later see how these interfaces can be used to increase the safety of our code.