Part 4 - The Trick and the Rest of (Vue-)Apollo
If you've landed here inadvertently and haven't read the first part, please do.
This tutorial has 4 parts:
- Part 1 - Getting Started
- Part 2 - Vue-Apollo and its Working Parts - Queries
- Part 3 - Vue-Apollo and its Working Parts - Mutations Part 4 - The Trick and the Rest of (Vue-)Apollo *(You are here now)*
In our other three parts, we got you started with a new Quasar project and Vue-Apollo. We also went over how to query for data and to mutate it. Now, we'll explain how it all works....the "trick".
Have a good look once more at /graphql/Todos/queries.js
. If you look at all of the queries, there is one directive they all have.
>>> @client
That is the trick to Apollo's client state management system. You can add the @client
directive to your queries and, of course, build the query within your local resolvers and instead of pulling the data from your GraphQL server, Apollo pulls the data from its local cache.
The really cool part about this is, you can mix and match your query's result fields with @client
and without it and Apollo will pull only that data from the cache and the rest from the server. We aren't using a server in our todo app, but this is what it might look like (taken from the Apollo docs).
const GET_LAUNCH_DETAILS = gql`
query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
isInCart @client
site
rocket {
type
}
}
}
`
Notice the @client
directive is only on the isInCart
field.
To make this happen, this is what your local resolver might look like.
resolvers: {
Launch: {
isInCart: (launch, _args, { cache }) => {
const { cartItems } = cache.readQuery({ query: GET_CART_ITEMS });
return cartItems.includes(launch.id);
},
},
},
If you'll notice, here only isInCart
is being givin a true/false value via the includes
of the cartItems
.
The Launch
object itself (fetched from the server), holds the rest of the needed information for the query to be "fullfilled". This is a key part of why the client state is advantageous over using Vuex. Your request for data, local or otherwise, comes from the same request. It simplifies reasoning about the data you are needing/ requesting a lot.
The setResolvers
and addResolvers
methods
In our todo app, we are defining our resolvers at the time the client is instantiated, by adding them in the constructor function of the client. As our app gets bigger, this might become combursome/ bloaty. If you want your resolvers to be loaded "just-in-time", you can use the setResolvers
and addResolvers
methods of Apollo client instead.
The difference between the two is setResolvers
will replace all of the resolvers stored in the client and addResolvers
will overwrite only those with the same name or add any new ones.
Fetching Data, What to Expect and @client(always: true)
If you are regular user of Apollo, you know about Apollo's "fetch policies". Apollo's fetch policies determine where data should be pulled with a bit of prioritization. If you don't know about them, please read this section of the Apollo docs in terms of what to expect with client state.
Bottom line is, you should make sure you are using the local cache, with the cache-first
policy. There is, however, a disadvantage to even this policy. If you have local calculations in your resolvers, they won't always be fired on each query. Apollo is smart about this, or is trying to be, so your app does the least amount of work necessary.
In some instances, however, you might want a calculation made on each request. If you do, there is a solution. The addition of the always
argument to the @client
directive.
It looks like this -> @client(always: true)
. Add this, and your local resolver will always fire with every Query made.
So, What's the Big Deal of All This Anyway?
You might be asking yourself this question and if you haven't noticed the advantages of adding Apollo and GraphQL to your front-end stack, let's discuss it now.
This is the big key advantage we said we'd cover at the beginning of Part 1.
The Magic of GraphQL
The heart of GraphQL is the ability to "query" for data and also to "mutate" it. This offers you the following advantages:
- You can query data to match only what you need for your components and component hierarchy.
- You can mutate data within your components at the point of where it actually should be mutated.
The above points offer a better seperation of concerns and responsibility within and among your components. And that, in turn, means clean, easy to reason about and thus, easily fixable and even testable code.
It's high cohesion and low coupling at its best!
Have a look at this diagram.
This is a simple diagram of our todo app's component hierarchy. As you see, we have the Todos query at the top. This app is simple, but imagine a bigger component with more child components built in. Imagine getting the right data to them, but only for what they exactly need. This is a huge challenge in working with a REST API, but it is the "built-in nature" of GraphQL. Once the query is "formed", fetched and returned, then all that needs to happen is the data be passed down via the component props. With "formed" we mean, the field hierarchy in your query can (and should) match your component hierarchy.
And, just as your queries perfectly match the component hierarchy, the mutations can be put in the components where data actually needs to be mutated, and completely encoupled from where the data is actually being mutated, as was done in our TodoToggle
and TodoEdit
components.
This affords a very clean, very simple front-end architecture and follows SRP and SoC, thus allowing for a much easier reasoning about your app's state logic. In other words, it simplifies your work as a front-end developer a lot.
Conclusion
Hopefully you enjoyed the articles and got some new perspectives on how a front-end app with Quasar (or Vue) could look using GraphQL, along with not needing Vuex for state management.
Let us know in the comments below about what you think of GraphQL, Apollo and using them within a Quasar/ Vue app with Vue-Apollo.
Thanks for reading!
Top comments (1)
Hi Scott, does this extension support Websocket from Apollo? if yes, can you show me how to config it