Wrapping REST API calls with Apollo Client: 'do-it-yourself' approach
Tags: graphql, javascript, apollo
Published: 2020-02-24 13:07:08

Sometimes, when your application is in the middle of the migration from REST to GraphQL API, you might find yourself in the situation where data your need is split between both APIs. Let's say when you were fetching data from REST API, you were storing it in your application global state - be it Redux, MobX or Vuex. But with the new shiny GraphQL API you don't even need to bother about creating a boilerplate for storing the response - Apollo Client will take care of this process for you! Does it mean with two APIs you need to stick to the old good boring solution and ditch Apollo Client cache? Not at all!

You can _wrap_ your REST API calls with Apollo and store results in Apollo cache too. If you have a large application and have many of them, you can use an [apollo-link-rest](https://www.apollographql.com/docs/link/links/rest/) library for this. In this article, we will create a basic DIY approach to this task to understand better how Apollo resolvers work and how we can use them in our application for good.

## What we're going to build?

As an example, we will use a Vue single-page application built on top of [Rick and Morty API](https://rickandmortyapi.com/). The good thing about this API is it has _both_ REST and GraphQL endpoints, so we can experiment with it a bit.

Let's imagine our application was using REST API exclusively. So, on the frontend side, we had a [Vuex](https://vuex.vuejs.org/) store and we called `axios` queries from Vuex actions to fetch **characters** and **episodes** from the API.

```js
// Vuex state

state: {
  episodes: [],
  characters: [],
  favoriteCharacters: [],
  isLoading: false,
  error: null
},
```

```js
// Vuex actions

actions: {
  getEpisodes({ commit }) {
    commit('toggleLoading', true);
    axios
      .get('/episode')
      .then(res => commit('setEpisodes', res.data.results))
      .catch(err => commit('setError', error))
      .finally(() => commit('toggleLoading', false));
  },
  getCharacters({ commit }) {
    commit('toggleLoading', true);
    axios
      .get('/character')
      .then(res => commit('setCharacters', res.data.results))
      .catch(err => commit('setError', err))
      .finally(() => commit('toggleLoading', false));
  },
  addToFavorites({ commit }, character) {
    commit('addToFavorites', character);
  },
  removeFromFavorites({ commit }, characterId) {
    commit('removeFromFavorites', characterId);
  }
}
```

I don't list Vuex mutations here as they're pretty much intuitive - we assign fetched characters to `state.characters` etc.

As you can see, we needed to handle the loading flag manually as well as storing an error if something went wrong.

Every single character in `characters` array is an object:

![Character pbject](https://i.imgur.com/ktiD0D1.png)

Now let's imagine our backend developers created a query for us to fetch episodes, but characters still need to be fetched via REST API. So, how we can handle this?

## Step 1: extend GraphQL schema

In GraphQL, anything we can fetch from the endpoint, must have a type and be defined in GraphQL schema. Let's be consistent and add `characters` to schema too. 'But how?' - you might ask, 'schema is defined on the backend!'. That's true but we can _extend_ this schema on the frontend too! This process is called `schema stitching`. While this step is completely optional, I would still recommend always define GraphQL type definitions for your entities even if they are local. It helps you if you use a [code generation](https://github.com/apollographql/apollo-tooling#apollo-clientcodegen-output) to create e.g. TypeScript types from the GraphQL schema and also it enables validation and auto-completion if you use an [Apollo plugin](https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo) in your IDE.

Let's create a new type for characters. We will be using `graphql-tag` to parse the string to GraphQL type:

```js
// client.js

import gql from "graphql-tag";

const typeDefs = gql`
  type Character {
    id: ID!
    name: String
    location: String
    image: String
  }
`;
```

As you can see, here we don't use all the fields from the `character` object, only those we need.

Now we also need to extend a `Query` type with the GraphQL `characters` query:

```js
// client.js

import gql from "graphql-tag";

const typeDefs = gql`
  type Character {
    id: ID!
    name: String
    location: String
    image: String
  }
  extend type Query {
    characters: [Character]
  }
`;
```

To stitch this part of the schema with the schema fetched from the GraphQL endpoint, we need to pass `typeDefs` to the GraphQL client options:

```js
// client.js

import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import gql from "graphql-tag";

const httpLink = createHttpLink({
  uri: "https://rickandmortyapi.com/graphql"
});

const cache = new InMemoryCache();

const typeDefs = gql`
  type Character {
    id: ID!
    name: String
    location: String
    image: String
  }
  extend type Query {
    characters: [Character]
  }
`;

export const apolloClient = new ApolloClient({
  link: httpLink,
  cache,
  typeDefs
});
```

## Step 2: Writing a query and a resolver

We need to define a GraphQL query with a `@client` directive to be called when we want to fetch characters. `@client` directive tells Apollo Client not to fetch this data from the GraphQL endpoint but the local cache. Usually, I keep queries in `.gql` files and add a `graphql-tag/loader` to webpack configuration to be able to import them.

```js
// characters.query.gql

query Characters {
  characters @client {
    id
    name
    location
    image
  }
}
```

But there is one issue: there are no characters in the local cache! How do we 'explain' to Apollo Client where it can get this data? For these purposes, we need to write a _resolver_. This resolver will be called every time we try to fetch characters to render them in our application.

Let's create a resolvers object and define a resolver for `characters` query

```js
// client.js

const resolvers = {
  Query: {
    characters() {
      ...
    }
  }
};
```

What should we do here? Well, we need to perform the same axios call we did in Vuex action! We will map response fields to our GraphQL type fields to make a structure plainer:

```js
// client.js

const resolvers = {
  Query: {
    characters() {
      return axios.get("/character").then(res =>
        res.data.results.map(char => ({
          __typename: "Character",
          id: char.id,
          name: char.name,
          location: char.location.name,
          image: char.image
        }))
      );
    }
  }
};
```

That's it! Now, when we call GraphQL `characters` query, our resolver will perform a REST API call and return a result for us. Bonus point: `$apollo.queries.characters.loading` property will change accordingly when the REST API call is in progress! Also, if some error happens on this call. the Apollo query `error` hook will be triggered.

## Conclusion

As you can see, having a part of the API on the REST endpoint doesn't prevent you from using Apollo Client and its cache. Any REST API call could be wrapped with Apollo resolver and its result can be stored to the Apollo cache which can simplify the migration process.