REST vs GraphQL from a frontend angle - part 3/3

05.08.2020 | 3 min read

Welcome to the third section of my tutorial series on using GraphQL in front-end development.

As a reminder, I was building a relatively small and simple app, which uses the Yelp API to search for different types of places based on location, search query and category. Yelp provides both REST and GraphQL APIs, which made it a very good fit for this experiment.

If you’d like a full recap:

Link to Part 1

Link to Part 2

A visual reminder of what we were building:

Performance

Two main pieces of information that were fetched from the API were categories and results. Let’s check the sizes of responses.

Response of all categories through
- REST API - 235.84 kB
- GraphQL API - 111.78 kB

Response of first 50 restaurants through
- REST API - 46.49 kB
- GraphQL API -  27.41 kB, even though I selected many keys in schema.

In REST, it’s possible to omit or fetch certain keys, but this of course requires more gymnastics and more effort. But, if you’re keen to see an example of what this would be like, you can do so here: https://jsonapi.org/

Tips and resolved issues

Accessing loading with refetch using the GraphQL approach was unavailable, so I changed the approach to the ApolloQuery component, which allowed me to get a loading prop on v-slot and refetch normally in methods:

javascript
<template>
  <div id="app">
    <ApolloQuery
      :query="GET_RESULTS"
      :notifyOnNetworkStatusChange="true"
    >
      <template v-slot="{ result: { data, loading }, query }">
        <AppNav @search="handleSearch($event, query)" />
        <div class="content">
          <div class="container">
            <AppHeader @search="handleSearch($event, query)" />
            <div class="main">
              <Map :results="data && data.search && data.search.business" />
              <Results
                :results="data && data.search && data.search.business"
                :loading="loading"
              />
            </div>
          </div>
        </div>
      </template>
    </ApolloQuery>
  </div>
</template>

<script>
  import AppNav from '@/components/AppNav.vue'
  import AppHeader from '@/components/AppHeader.vue'
  import Map from '@/components/Map.vue'
  import Results from '@/components/Results.vue'
  import { GET_RESULTS } from './graphql/search.query';
  export default {
    name: 'App',
    components: {
      AppNav,
      AppHeader,
      Map,
      Results
    },
    data() {
      return {
        GET_RESULTS
      }
    },
    methods: {
      handleSearch({ term, category }, query) {
        query.refetch({term, category});
      }
    }
  }
</script>

Using @client allowed me to get data straight from the cache.

javascript
import gql from 'graphql-tag';

export const GET_ZOOM = gql`
  {
    zoom @client
  }
`
export const GET_CENTER = gql`
  {
    center @client {
      lat
      lng
    }
  }
`

Also, I used cache-and-network fetch policy to get cache data after I already had it:

javascript
const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  defaultOptions: {
    // apollo options applied to all queries in components
    $query: {
      loadingKey: 'loading',
      fetchPolicy: 'cache-and-network'
    }
  }
})

One of the most tricky things that I had to solve was connecting specific queries that resulted in search_XYZ keys in cache to result in a search key that was replaced every time I used this query. I managed to do that with @connection:

javascript
import gql from 'graphql-tag';

export const GET_RESULTS = gql`
  query GetResults($term: String, $category: String) {
    search (
      term: $term,
      categories: $category,
      limit: 50
      latitude: 52.237022,
      longitude: 21.050440,
      radius: 15000
    ) @connection(key: "search") {
      total
      business {
// ...

The other tricky thing, as I didn’t use any mutations in this app, was the fact that I had to pass a resolver key with an empty object when creating a new ApolloClient, along with cache and typeDefs. Without this, the app was producing errors:

javascript
export const apolloClient = new ApolloClient({
  link: authLink.concat(httpLink),
  cache,
  typeDefs,
  resolvers: {}
})

Final thoughts

After having completed this project in GraphQL, I’m still not a big fan, but I see great potential in it. I think that I need to become more fluent to fully appreciate it, because my initial work required a lot more researching and problem-solving than I would traditionally do in REST.

I encourage you to check it out and see for yourself, though. It may appeal to you. Also, it’s important to understand how it works if you want to learn tools like Gatsby.

REST approach: https://github.com/BartoszBilejczyk/yelp-rest

GraphQL approach: https://github.com/BartoszBilejczyk/yelp-graphql

You may also like these posts

Start a project with 10Clouds

Hire us