Typing and mocking a GraphQL API server with Apollo
Tags: graphql, apolloserver
Published: 2021-03-26 03:52:01

Previously in this series, I made a [GraphQL server that returns nothing](https://pbrg9.sse.codesandbox.io/). Now I'm going to design the schema (**type** defintions) and set up **mocking** functionality. Since I have yet to build the resolvers (which retrieve data from a database or other sources), the server will now return *mock data* based on the schema.

## Overview

I'm building the API for a hypothetical app that displays my favourite music releases. The API server shall return **a list of music releases**; each list item shall have a title, an artwork image, artist name(s), and URL to play on Spotify.

I borrowed the basic models and descriptions from [Spotify Web API official documentation](https://developer.spotify.com/documentation/web-api/reference/#objects-index) via an [app that converts Spotify API’s objects into GraphQL schema](https://dev.to/ekafyi/converting-spotify-web-api-objects-to-graphql-schema-object-type-definitions-h04) I’d built earlier, liberally simplified and modified for this purpose.

TL;DR? View (and fork) the [live CodeSandbox app](https://codesandbox.io/s/mock-music-releases-server-kqt0s) ↓

{% codesandbox kqt0s %}

## Part 1: Typing

A **schema** describes our GraphQL API’s data models, relations, and operations. It is comprised of **type definitions** written in a syntax called the **Schema Definition Language** (also called the "GraphQL schema").

So... what exactly are these **types** that we define? GraphQL recognizes a number of type categories.

- Entry-point types: `Query` and `Mutation`
- Object types
- (Default/built-in) Scalar types
- Custom scalar types
- Enum types
- Input types
- Abstract types: unions and interfaces

We are only using some of those now, but I'm including references at the end of the post if you’d like to learn more.

Quick copypasta of types we're going to use:

```graphql
# Query type
type Query { ... }

# Object types
type Artist { ... }
type Album { ... }
type Track { ... }
type Image { ... }

# Enum type
enum AlbumType { ... }

# Interface type and object type that implements it
interface Release { ... }
type Album implements Release { ... }

# Union type (we're discussing but _not_ using this)
union Release = Album | Track
```

### Object types
An object type represents *a kind of object* (...who would’ve thought? 😬) containing *a collection of fields* (properties) that can be fetched from some data source(s). A GraphQL schema is usually largely comprised of these types.

For our schema, let’s define the object types `Artist`, `Album`, `Track`, and `Image` (truncated for brevity).

```graphql
type Artist {
  id: ID!
  name: String!
  images: [Image!]
}

type Album {
  id: ID!
  name: String!
  artists: [Artist!]!
  images: [Image!]
  release_date: String
}

type Track {
  id: ID!
  name: String!
  artists: [Artist!]!
  album: Album!
  track_number: Int
  preview_url: String
}

type Image {
  url: String!
  height: Int
  width: Int
}
```

- An object type must contain at least one field. The object type `Artist` contains fields called `id`, `name`, and `images` (and so on).
- The field type can be...
	- a **scalar type** (`ID`, `String`, `Int`—details below),
	- or another **object type** (eg. in the `Track` object type, the `album` field contains an `Album` object). 
- The exclamation mark `!` indicates a **required field**; lack of which indicates nullable.
- The square brackets `[]` indicates a **list/array** of the type inside the brackets.
	- eg. `[String]` = a list of strings, `[Artist]` = a list of `Artist` objects.
- `artists: [Artist!]!` means the `artists` field is required and cannot be null (right-side/outer exclamation mark). It cannot return an empty array, either; it must contain at least one `Artist` object (inner exclamation mark).

Note that the SDL is agnostic with regards to data sources; we don’t have to define object types and fields in relation to any database table/column structure.

### Query type

This is *the only required part* of a schema. Technically, it’s possible to have [a server without a single object type](https://dev.to/ekafyi/serving-nothing-through-graphql-58cc). But we will get an error if the `Query` type does not exist.

`Query` and `Mutation` are GraphQL’s special *top-level, entry point* types. Clients, ie. front-end apps, can only retrieve (read) data through `Query` and store/update/delete (write) data through `Mutation`. 

Unlike a REST API, a GraphQL API has no multiple endpoints. Clients don't send a GET request to `/albums` for a list of albums and another to `/artists/:id` for the artist data; they simply *send a single query* with a list of fields they need, which are the fields in our `Query` type.

```diff
type Query {
+  albums: [Album!]
+  tracks: [Track!]
}
```

With our schema above, clients can query for `albums`, which will include the relevant artists and images data (if they include those fields in the request). Likewise for `tracks`. *But* they cannot get a list of artists or images, since our type definition does not include such fields.

Like object types and `Mutation`, `Query` fields can accept arguments.

```diff
type Query {
  albums: [Album!]
  tracks: [Track!]
+ album(id: ID!): Album
+ track(id: ID!): Track
}
```

The last two fields enable clients to query a single album or a single track by passing an `id` as argument.

Note: We are not discussing `Mutation` here, but it's essentially `Query`'s counterpart for POST/PUT/DELETE operations.

### Scalar types

Some fields above have types like `Int` and `String`—these are the default/built-in **scalar types**, which are GraphQL schema’s [primitive types](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) equivalent. There are five scalar types: `ID`, `String`, `Int`, `Float`, `Boolean`. 

We can also define our own [custom scalar types](https://www.apollographql.com/docs/apollo-server/schema/custom-scalars/). Apollo Server provides the `GraphQLScalarType` constructor class to facilitate defining our custom scalar logic, which we then pass to a custom resolver. I'm going to discuss this part in a separate post.

### Enum types

Like other typed languages, GraphQL SDL has an [enumerated type](https://en.wikipedia.org/wiki/Enumerated_type), a.k.a. **enum**. An enum field type can only have one of the predefined string values.

The Spotify API’s [AlbumObject](https://developer.spotify.com/documentation/web-api/reference/#object-albumobject) has an `album_type` field, originally typed `String`, whose value is one of *"album"*, *"single"*, or *"compilation"*. Let’s define it as an enum type in our schema.

```diff
type Album {
  id: ID!
  name: String!
  artists: [Artist!]!
  images: [Image!]
  release_date: String
+ album_type: AlbumType
}

+ enum AlbumType {
+   ALBUM
+   SINGLE
+   COMPILATION
+ }
```
In addition to type safety (when querying and displaying on the frontend, when passing as query argument, as well as when making mutations), using enum types also gives us the benefit of intellisense/autocompletion in supported IDEs.

![IDE showing available AlbumType values](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inoy6h2re009k2cdtuec.png)

Note that the enum values have to be in UPPERCASE as per GraphQL specs. If the values in the data source (eg. database) are in lowercase, we can map them in a *custom resolver function*. Not only for mapping cases of course, we can use it eg. to map colour shorthands into corresponding hex values, as shown in [the Apollo Server documentation](https://www.apollographql.com/docs/apollo-server/schema/schema/#internal-values-advanced).

### Union and interface types

The last types we're going to discuss are the abstract types. Essentially, they represent *a type that could be any of multiple object types*. Quick definitions by [GraphQL Ruby](https://graphql-ruby.org/type_definitions):
- **Interface:** "a list of fields which may be implemented by object types"
- **Union:** "a set of object types which may appear in the same spot"

(🤔 ...huh?)

We're going to see how *either one* could be used in our schema to describe "a music release, which could either be an album or a track".

#### Option A — with union
One possible approach is by defining **a union type** called `Release`, which could either be an `Album` or a `Track` object type. Then we add a field called `releases` to the query, which returns a list of `Release` objects. 

```diff
+ union Release = Album | Track

type Query {
  albums: [Album!]
  tracks: [Track!]
+ releases: [Release!]
}
```

Notes:
- The `Album` and `Track` definitions stay the same.
- A union can only consist of two or more *object types*, not scalar or any other types. 

Unexpectedly (to me), this type is *not* intended for mixed scalar and object types, eg. a field that could be an integer or a particular object type. We need *custom scalars* for that instead. Related discussions:
  - https://github.com/graphql/graphql-js/pull/242
  - https://github.com/graphql/graphql-spec/issues/215

#### Option B — with interface
Alternatively, we can define **an interface type** called `Release`, which is then *implemented* by the `Album` and `Track` object types. It contains the fields `id`, `name`, and `artists`, which both `Album` and `Track` have. Like in option A, we add a field called `releases` to the query, which returns a list of `Release` objects. 

```diff
+ interface Release {
+   id: ID!
+   name: String!
+   artists: [Artist!]!
+ }


- type Album {
+ type Album implements Release {
  id: ID!
  name: String!
  artists: [Artist!]!
  images: [Image!]
  release_date: String
}

- type Track {
+ type Track implements Release {
  id: ID!
  name: String!
  artists: [Artist!]!
  album: Album!
  track_number: Int
  preview_url: String
}

type Query {
  albums: [Album!]
  tracks: [Track!]
+ releases: [Release!]
}
```

Another unexpected-to-me finding: We still have to include the common interface fields in the object type fields (in this case `id`, `name`, `artists`). Yes, we write `id: ID!` three times and they have to be identical.

#### So... are they interchangeable?

I ended up in a rabbit hole researching these two types’ difference in usage, but it *is* an interesting topic! I will probably revisit this in a separate post.

TL;DR + IMO: They are *conceptually different, but share similar characteristics* as abstract types, which enable them to solve the same problems (such as our `Release` type) albeit in different ways.

On a more practical level, when querying, we use [inline fragments](https://graphql.org/learn/queries/#inline-fragments) differently with union and interface types.

A **union type** does not know what fields each object type does/does not have. So we have to specify *all fields inside the inline fragments*, including the identical ones (eg. `name` and `artists`).

```graphql
query GetUnionReleases {
  latest_releases {
    ... on Album {
      # name and artists fields here...
      name
      artists
      images
    }
    ... on Track {
      # ...name and artists fields again here
      name
      artists
      album
    }
  }
}
```

Meanwhile, with an **interface type** we can specify the common fields (the interface’s fields) outside the fragments. The `Release` interface *always* contains `name` and `artists` fields, so we can query them outside the inline fragments.

```graphql
query GetInterfaceReleases {
  latest_releases {
    # interface’s fields
    name
    artists
    # object type specific fields in the fragments
    ... on Album {
      images
    }
    ... on Track {
      album
    }
  }
}
```

These queries will be made from the clients that consume our API, but you can try it from the server app’s GraphQL Playground IDE. For this app, I decided to use the **interface type** to define `Release` (option B).

## Half-time

Let’s see our code so far.

```js
// index.js
const { ApolloServer, gql } = require("apollo-server");

const typeDefs = gql`
  type Query {
    albums: [Album!]
    tracks: [Track!]
    releases: [Release!]
  }
  # ... the rest of the schema
`;

const server = new ApolloServer({ typeDefs });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});
```

The server is up and running, and the GraphQL Playground IDE shows the documentation and autocomplete feature based on our type definitions.

![GraphQL Playground IDE showing releases query with autocomplete fields and list of available objects on the right pane](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qs1wj6vbvbxghkasseg7.png)

We can also use [Apollo Studio](https://www.apollographql.com/docs/studio/) to access—among other things—a more powerful and dev-friendly query builder.

![Apollo Studio query builder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kg76gvghh1zewu1mmy7f.png)

Above we can see the `Release` interface detail, including the types that implement it. When I select the `images` field under `Album`, it automatically builds the inline fragment in the query. Smooth! 😎

But when we *run* the query, the server returns `null` because we have not set up the resolvers, which will return each field value. Fortunately, Apollo Server has an extensive mocking feature based on our type definitions.

## Part 2: Mocking

### Mocks out of the box

Add a `mocks: true` option when creating a server instance to enable the **default mock resolvers**.

```js
const server = new ApolloServer({
  typeDefs,
  mocks: true,
});
```

When we send the query... the returned data is no longer `null`.

![GraphQL IDE showing mock data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fugviidso5jp5ldfr8hh.png)

Let's query some more data...

```graphql
query Query {
  releases {
    id
    name
    artists {
      name
    }
    ... on Album {
      album_type
      name
      release_date
    }
    ... on Track {
      name
      track_number
      explicit
    }
  }
}
```

...and here is the returned mock data.

```json
{
  "data": {
    "releases": [
      {
        "id": "f4570e2d-9221-4a8e-ae5d-6cf0bcb3c10e",
        "name": "Hello World",
        "artists": [
          {
            "name": "Hello World"
          },
          {
            "name": "Hello World"
          }
        ],
        "track_number": -65,
        "explicit": false
      },
      {
        "id": "be550404-7bad-46c7-a314-cb88ceec1710",
        "name": "Hello World",
        "artists": [
          {
            "name": "Hello World"
          },
          {
            "name": "Hello World"
          }
        ],
        "album_type": "COMPILATION",
        "release_date": "Hello World"
      }
    ]
  }
}
```

- `ID` is mocked as random string uuid. Nice!
- `String` is mocked as `Hello world`.
- `Int` is randomly generated.
- `Boolean` and enum types are correctly typed!
- Array types always return 1-2 items (depending on the number of fields).
- We don't have custom scalar type here. If we did, we would have to pass a custom resolver for the mock to work.

This is pretty cool, considering it takes literally five seconds to implement. But let's make the mock data resemble our expected data better.

### Custom mock resolvers

Pass an object to the `mocks` option to enable **custom mock resolvers**. As the name suggests, they are identically shaped to the actual resolvers, both of which correspond to the schema. Each property is a *function* that returns the corresponding mock value.

For example, this replaces *"Hello world"* with *"My custom mock string"* for the `String` scalar type value.

```diff
- const mocks = true;
+ const mocks = {
+   String: () => "My custom mock string"
+ };

const server = new ApolloServer({
   typeDefs,
+  mocks,
});
```

But the album, track, and artist names are all `String`. Does not make much sense if they all return *"My custom mock string"*, does it?

Luckily, we can mock our object types the same way. Remove the `String` function and add `Album`, `Track`, and `Artist` functions.

```diff
const mocks = {
-   String: () => "My custom mock string"
+   Album: () => {
+     return { name: () => "Album Title" };
+   },
+   Track: () => {
+     return { name: () => "Track Title" };
+   },
+   Artist: () => {
+     return { name: () => "Artist Name" };
+   }
};
```

Let's send another query and see the returned mock data.

```graphql
query Query {
  releases {
    __typename
    name
    artists {
      name
    }
    ... on Album {
      album_type
    }
    ... on Track {
      track_number
    }
  }
}
```

```json
{
  "data": {
    "releases": [
      {
        "__typename": "Track",
        "name": "Track Title",
        "artists": [
          {
            "name": "Artist Name"
          },
          {
            "name": "Artist Name"
          }
        ],
        "track_number": 33
      },
      {
        "__typename": "Album",
        "name": "Album Title",
        "artists": [
          {
            "name": "Artist Name"
          },
          {
            "name": "Artist Name"
          }
        ],
        "album_type": "ALBUM"
      }
    ]
  }
}
```

Note that although we only pass the `name` function, the mock logic uses the default mock logic for the remaining fields (eg. `track_number` in `Track`, `album_type` in `Album`). Nice!

### MockList

Lists—such as `releases` and `artists`—return two items by default. What if we need different number of items mocked? Apollo Server provides the `MockList` constructor to customize the number of items in a list.

```diff
const { 
   ApolloServer,
+  MockList 
} = require("apollo-server");

const mocks = {
  Album: () => {
-   return { name: () => "Album Title" };
+   return { name: () => "Album Title", artists: () => new MockList(1) };
  },
  
  Track: () => {
-   return { name: () => "Track Title" };
+   return { name: () => "Track Title", artists: () => new MockList(1) };
  },
  
+ Query: () => {
+   return { releases: () => new MockList([0, 15]) };
+ }
}
```

- Although an album or track can have more than one artists (eg. featuring/collaboration), most albums and tracks just have one artist. Let's add an `artists` mock resolver to `Album` and `Track` which returns a `MockList` object. The integer argument `1` means always return one item.
- Meanwhile in `Query`, we add `releases` that return between 0 to 15 items, using the array argument `[0, 15]`. This is particularly useful for testing and UI development/prototyping—what an empty state looks like, what the pagination looks like, what an "awkward" number of item (eg. just 1) looks like.

Now when we run our query, the returned mock data looks like this.

![GraphQL IDE showing more than 2 releases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7pnplf4ivpbr5h94vu2.png)

It's still not ideal (`track_number: -60` anyone? 😬), but we've got some mock data to test and start developing the UI/frontend app with.

## Putting it together

In this post, we define our API’s types and their relations (`Query`, object types, enums, interface) in the GraphQL schema language and pass it as template literal to Apollo Server’s `gql` parser. Then we use the built-in `mocks` option so our server returns mock data based on our type definitions. We use custom mock resolvers to customize our mock data behaviour.

Stay tuned for the next post in this series, where I'm going to improve my mocks with `casual` fake data generator!

## References

- [GraphQL.org - Introduction to GraphQL](https://graphql.org/learn/)
- Official documentation: 
  - [Introduction to Apollo Server](https://www.apollographql.com/docs/apollo-server/)
  - [Schema and types](https://www.apollographql.com/docs/apollo-server/schema/schema/)
  - [Mocking](https://www.apollographql.com/docs/apollo-server/testing/mocking/)
  - [API reference](https://www.apollographql.com/docs/apollo-server/api/apollo-server/)
- [Apollo Odyssey Fullstack Tutorial](http://odyssey.apollographql.com/)
- [How to GraphQL Fullstack Tutorial](https://www.howtographql.com/)