loading...
Quasar

Quasar and Apollo - Client State without Vuex - Part 2

smolinari profile image Scott Molinari Updated on ・7 min read

Part 2 - Vue-Apollo and its Working Parts - Queries

If you've landed here inadvertently and haven't read the first part, please do.

This tutorial has 4 parts:

Apollo and Vue

Apollo is an awesome GraphQL client package and if you look at their docs, you'll notice it's mainly aimed at React. However, we in the world of Vue, thankfully, have a nice Vue plug-in package called vue-apollo, which helps offer a sleek API into Apollo within Vue (and of course Quasar).

The Flow of Data in GraphQL

Below is a diagram showing the flow of data through a Quasar/Vue/Apollo app.

If you are familiar with Vuex, the flow above might look somewhat familiar to you. With Apollo, you have Queries that pull the data from "a source" into the view, similar to Vuex's getters. And, you have mutations that alter the data in that same "source", similar to Vuex's setters.

Fact of the matter is, this is the magic of GraphQL. While with other solutions the devs are fumbling around with GET and POST requests to a REST API to get data and mutate data between the client and the server and trying to figure out the right reply signature to make things semi-efficient and also how to best get it into their store, GraphQL is already just doing its thing. As soon as the mutation or query is written (of course, with the server or client side resolvers having been developed too), you are getting the responses you expect, and more importantly, in the form you need them.

If you'll notice, in the middle of the diagram is a "store". That's Apollo's caching mechanism. Our litte todo app is only using that store and we won't be making calls to a server. However, if we did want data from or mutate it on the server, we wouldn't need to change our queries and mutations very much to make it happen. This will be explained better later.

For now, just realize the queries and mutations you are about to see are the same or similar for any GraphQL system or for calling from any "resource". That, in turn, is also a great benefit of GraphQL. It is is a specification and with a specification, you get standardization. And standardization means, less cognitive load for onboarding new devs, and that means better and faster solutioning.

If you are at all in the know about GraphQL, we probably didn't need to mention this. But, if you are new to GraphQL, welcome to a new paradigm of data fetching and mutation.

Priming The Cache (or Store)

Before we can begin, one thing is very important. If we are going to be addressing the Apollo Cache ourselves to use it as our single-source of truth, we need to "prime" it. It's almost like how Vue needs data to be initialized in a component, before it can be reactive.

In our example todo app, if you go to /src/quasar-app-extension-apollo/apollo-clent-hooks.js you'll see how this is done.

We've got our todos array, the networkStatus object and a filters object set up.

Again, this needs to happen, but only for the data we want to store within Apollo for client-side state.

Before we continue to dig in, if you don't have any or very little knowledge of GraphQL, you should learn a bit more about it. Otherwise, we might be losing you at some point below and we don't want to have that. "How to GraphQL" is a good tutorial to get started and learn about the basics of GraphQL. For now, you only need to learn about the client side concepts and the concept of resolvers on the server and what they mean in response to queries and mutations.

Queries and the Apollo Option

In our todo app, we only have two queries. One for the list of tasks, our "todos", and one for the list of filters of the todo list (All, Completed and Active).

The filters could very well have been hard-coded into the components, but we've done it this way to demonstrate two things:

  1. Another way to instantiate the cache with values you need (as shown above).
  2. To show that you could theoretically also pull the list of filters from the server too and not have them hard coded in your component.

By that last point, we mean that you could very well pull the data of what filters offer, like the text in them, from the server, making the filterBar very dynamic for say, i18n purposes.

Let's start with the query for filters.

Go to the FilterBar component. You'll see it is a normal component with a QBtn.

So what do we have here?

In...

  • lines 1-14 we have our <template> section, where we are building our possible filters via a v-for loop of the queried data.
  • line 17, we are importing our queries and mutations.
  • lines 19-26, we are initializing our component's data/ state.
  • lines 28-30, we are using the apollo option to inject our query into the component in order to get the list of filters from our store (more on this later).
  • lines 32-43, we have a method with our mutation (more on this in Part 3.)

Important to note is the apollo option. This is how we can "connect" our component's data to the data we are querying for. The property should always match a property within the component's data and vue-apollo will automatically assign the result object to the matching data property. This is my personal favorite way of adding queries. There is another couple of ways, one we'll cover below.

One more note, should you not want to match your data's naming, you can also assign the property in Apollo to your data property via the update option of Apollo. For instance, let's say our data prop wasn't filters, but rather fooFilters. You could do something like this.

1  apollo: {
2    filters: {
3      query: gql`query {
4        // our query would be here. 
5      }`,
6      update: data => data.fooFilters
7    }
8  }

Getting back to the query, if you open up our queries.js file under the /graphql/Todos folder, it looks like this.

1  export const getFilters = gql`
2    query GetFilters {
3      filters @client {
4        name
5        label
6        active
7      }
8  }
9  `
10

It's a very simple GraphQL query.

Now let's go one up in the hierarchy and look at our TodoList.vue file.

So what do we have here?

In...

  • lines 1-10, we have our v-for loop building our todo list of tasks.
  • lines 13-14, we are importing our queries and components.
  • lines 16-25, we intializing our component's data.
  • lines 27-30, we are injecting our queries into our apollo option.
  • lines 32-45, we have a computed method to calculate our "visible" todos.
  • lines 47-58, we have a watcher on our todos, so when they are all "completed", we give the user a nice motivational message.
  • lines 60-65, we have another watcher watching the filters data and setting the active filter accordingly.

If you've noticed, we are, once again, querying for the filters. This is so we can filter the todo list on the active filter.

Here is a challenge. Can you imagine a different way of doing this? If yes, explain what your method would be in the comments below!

So that is how we query in vue-apollo. However, that is only one way. There are others....

The Vue-Apollo <ApolloQuery> Component

Another way to do an Apollo query is via the vue-apollo query component. If you look at the /components folder, there is an alternate TodoList.vue file called TodoList.alt.vue.

Open it up and you'll see this.

So what is different here?

In...

  • lines 2-12, we have the <ApolloQuery> component.
  • lines 40-42, we are only injecting the filters query, because our todos query is above in the template now.
  • line 44, we changed our computed to a method. Theoretically, it could have stayed a computed, but it looks a bit cleaner this way.
  • line 59, we had to change our watcher to watch all data in the <ApolloQuery> component.

There are two other methods to get the query into the component.

  1. By assigning the query to a data prop.
1  <template>
2    <ApolloQuery
3      :query="todoQuery" //<--here
4    >
....
5    </ApolloQuery>
6  </template>
7
8  <script>
9  import { queries } from 'src/graphql/Todos'
10 import Todo from 'components/Todo'
11
12 export default {
13   name: 'TodosList',
14   components: { Todo },
15   data () {
16     return {
17       filters: [],
18       activeFilter: ''
19       todoQuery: queries.getTodos //<--our query
20     }
21   },
22

Using '.gql' Files

Using 'gql' or 'graphql' files has a slight advantage over putting the code in JavaScript, as we've been doing. With them in their own files, we can take advantage of Webpack and the GraphQL AST (your tree of queries) doesn't get built on the client, but rather on the server. As your application gets larger, you might want to consider this approach to save compute time on the client.

This is what using a .gql file might look like.

1  <template>
2    <ApolloQuery
3      :query="require('../graphql/GetTodos.gql')"
4    >
....
5    </ApolloQuery>
6  </template>

In order for this to work, however, you must add the appropriate Webpack loader into quasar.conf.js. You'll need to add this module rule.

chain.module.rule('gql')
   .test(/\.(graphql|gql)$/)
   .use('graphql-tag/loader')
   .loader('graphql-tag/loader')

Example code was added to the codesandbox to the app repo. See TodoList.alt2.vue and the quasar.conf.js files.

Conclusion

So, this is how you can query for data within Quasar/ Vue with the vue-apollo package and with Apollo Client. There is more advanced things to learn about vue-apollo and querying, like how to offer variables with the query and more. You can find out more in the resources below.

Resources:

vue-apollo docs: https://apollo.vuejs.org/guide/
GraphQL docs: https://graphql.org/learn/
Apollo Client docs: https://www.apollographql.com/docs/react/

In Part 3 we'll be going over mutations and closing the loop between pulling data and relating it to our components and also manipulating that same data and seeing the manipulations happening.

Posted on Apr 5 by:

smolinari profile

Scott Molinari

@smolinari

Just a hobbyist programmer with an eye on new technologies. Currently helping Quasar Framework, because....well.....because it's awesome! :-)

Quasar

One source code for all platforms simultaneously through Quasar CLI with all the latest and greatest best practices out of the box. Focus only on your app's features and forget about the boilerplate around it.

Discussion

markdown guide