Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
February 24, 2020 01:07 pm GMT

Wrapping REST API calls with Apollo Client: 'do-it-yourself' approach

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 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. 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 store and we called axios queries from Vuex actions to fetch characters and episodes from the API.

// Vuex statestate: {  episodes: [],  characters: [],  favoriteCharacters: [],  isLoading: false,  error: null},
// Vuex actionsactions: {  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

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 to create e.g. TypeScript types from the GraphQL schema and also it enables validation and auto-completion if you use an Apollo plugin in your IDE.

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

// client.jsimport 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:

// client.jsimport 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:

// client.jsimport { 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.

// characters.query.gqlquery 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

// client.jsconst 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:

// client.jsconst 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.


Original Link: https://dev.to/n_tepluhina/wrapping-rest-api-calls-with-apollo-client-do-it-yourself-approach-4i3p

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To