Hi @Akryum!
Apollo's cache and vuex's store seem to have some overlap in terms of usage, so how would you recommend using vue-apollo and vuex together?
The usual approach with REST would be to have a folder that holds the api service and a folder that holds the state store (this is how Gitlab does it), and then have it all come together inside the main component; another approach is to have the api service/calls integrated into vuex actions.
My first instinct was to use the vanilla apollo plugin and follow one of the above approaches. But then I realized that graphql is a different paradigm - the component queries and mutates its own data - hence why you tightly integrated query fetching inside components. So it'd be great to have the creator of the plugin weigh in on best practices.
Thanks
// This is to related issue #7, so I'll cc people who might be interested in this discussion @PierBover @sebT @RichAyotte @smolinari
@alidcastano I'll tell you from my experience (I'm building a chat application, something like Slack)...
I was also a big fan for Vuex when using REST. But after using Apollo client, I no longer needs Vuex. Vuex is some sort of Flux/Redux implementation. It's already built in Apollo client.
All data I need is inside the Apollo cache, and I access it directly. You have full access to that cache so that you can insert, update and do everything with it.
So after playing with Apollo client cache I really think there is no need for Vuex (in my case). But in some cases, I still need a simple centralized store to save some information like selected_user_id. For that, I use Vue-stash
Again, it's from my experience. Maybe it doesn't fit for you. What are you building?
Waiting for expert opinions...
Side note: one of Apollo Client 2.0 good news is that we can write a vuex implementation of the apollo cache. That may open nice possibilities like getting apollo data directly in getters.
Side side note: the default in-memory cache is no longer using Redux (so you don't have Redux + Vuex anymore in your app).
Yeah I'm eager to use apollo-client 2.0, it has so really nice features.
To answer your question from my experience @alidcastano, there is no problem with using vuex + apollo. It just depends on how you use them.
If your application uses a REST api, the Gitlab approach is very nice. You query your API, and store data in any format you wish in your store for further use in your application, vuex is not only used as a local app state manager but can also be used as a cache manager.
Apollo is made to interact with your graph API. You send a query, the result is normalized by stored in apollo's redux store, is a similar way the Gitlab approach uses vuex for.
So apollo (vue-apollo for easier integration) is the tool of choice to interact with your API data, locally and remotely. But you still need a tool to manage your local application state. Sure, components have state and you can pass is down to children components. But sometimes you need transversal data. The is where vuex comes in.
For instance you can have a currently selected item id (that comes from apollo) stored in vuex, so that you can request this item via apollo in different components. By default, if a query was already fetched, apollo will use the redux store instead of re-querying it, so don't hesitate to do the same query multiple times.
TL;DR: Use vue-apollo to interact with your graph API. Use vuex to manage your local app global state. But you shouldn't duplicate data between vuex and apollo.
@gijo-varghese What's interesting is that Facebook created the Flux pattern and they also created Graphql... But yea I had a similar feeling that most aspects of Vuex are not necessary given that Apollo 2.0 comes with its own cache/store implementation.
As @SebTSo mentioned, Grapql can manage component local and remote state, but you still need a way to manage application state - which is where Vuex getters (or vue stash) could come in. 馃憤
I'll go ahead and close this issue since the gist of the question has been answered but feel free to still add to the discussion. Thanks again guys.
@alidcastano: The flux pattern and graphql are made to work together. The apollo client implements the flux pattern (because it uses redux, which is derived from flux).
The only question I have now is what would be the best way to do keep everything in sync
Actually, you shouldn't imo. Apollo should be the only one handling data from API. And vuex/vue-stash shoud only manage your local data (dumb example: a click counter).
But sometimes you need to reference API data with vuex. Let's say your app is in logged mode. You often need to access the current user's name for instance. You can store the userId in vuex, but every time you need to access the name, you should to an apollo query in your component.
The confusion is caused by th fact that 2 state managment tools are present in the same app. Apollo 2 will be much more intuitive because you'll be able to use vuex for local state and API data.
EDIT: Maybe you meant "in sync" with your API data, not in sync between vuex & apollo. That makes my answer unrelevant.
But if that's what you meant, you can specify a fetchPolicy for each request so that a network request is done everytime.
A fullstack example with real world cases would be super helpful or even a video tutorial in Udemy. Otherwise it feels like Vue is falling behind React which has a more mature ecosystem and examples in terms of GraphQL. Ideally there should be an official implementation of Apollo that replaces Vuex
@lobosan Did you take a look at https://github.com/Akryum/devfest-nantes-2017 ?
@Akryum
Side note: one of Apollo Client 2.0 good news is that we can write a vuex implementation of the apollo cache. That may open nice possibilities like getting apollo data directly in getters.
Wouldn't it make sense to drop vuex altogether and put stuff like local session state in apollo client as well?
@beebase nope. Apollo's job is to interact with a server via the graphql queries (or mutations & subcriptions). It stores that data locally in your app so that you don't have to query the server every time you need some data. To do so, it uses a state management library (redux in apollo-client 1.x) to manage a representation of the data in your app's memory.
Most people using vue-apollo already use vuex in their application to manage their app state (usually non-persisted state). So it makes sense to drop redux & tell apollo to store its data in vuex instead. Because you're already using it for your app local state, you don't need 2 state management libraries.
Using apollo as a client-side state management system is becoming more and more popular, thanks to https://github.com/apollographql/apollo-link-state
@SebT
So it makes sense to drop redux & tell apollo to store its data in vuex instead.
Do you mean like apollo directly writing into vuex state, or apollo triggering vuex actions, that write to vuex state?
Either way, with apollo 2.0, you can make local state persistent, control interaction with server, control caching, and it has the same reactivity in components like vuex has.
So I don't see why I would still use vuex.
The only thing that's a bit funky, is writing queries inside components as opposed to vuex. It feels like you might end up with duplicate code and I wonder how maintainable this is in very large apps.
I guess you could build vue query components (factories) to avoid repetitive graphql. But then again, this might lead to over-fetching again 馃槙
@beebase Write .gql files 馃槃
@Akryum yes I am, but at the same time that probably only makes sense for queries/mutations being repeated in multiple components?
@Akryum - do you not recommend to have the GQL inside of each .vue component?
Scott
@smolinari @Akryum me too waiting for an answer for that I've created a separate issue for that https://github.com/Akryum/vue-apollo/issues/228
Is there any update on this? Defining this would help with defining the best practice to link Apollo into Vue + Vuex. I have created a project with just Apollo and Vuex. If it works out fine I'll make a boilerplate. Personally it doesn't make sense to directly write the queries on components. Since these are asynchronous process it could be best handled in actions in Vuex and only the result can be returned to the component. Would help in separation of HTTP request logic.
Personally it doesn't make sense to directly write the queries on components.
This is the concept behind Apollo. Co-locate the data requirements of each component with the component itself, like you do with presentation/logic. Apollo does the rest!
The Apollo community is also moving towards apollo-link-state for local client-only data instead of relying on Redux/Vuex/etc.
For example, I recently wrote a reasonably big app which doesn't need vuex.
@beebase
The only thing that's a bit funky, is writing queries inside components as opposed to vuex. It feels like you might end up with duplicate code and I wonder how maintainable this is in very large apps.
I guess you could build vue query components (factories) to avoid repetitive graphql. But then again, this might lead to over-fetching again 馃槙
My thoughts were the same and my approach has been to store anything related to apollo in a mixin and then include that in my individual components.
For example I have an allFruitQuery mixin that allows me to simply use 'allFruits' in any component by including my mixin
`import allFruit from '@/graphql/queries/allFruit.gql'
export const allFruitQuery = {
data () {
return {
allFruits: ''
}
},
apollo: {
allFruits: allFruit
}
}`
More of this! Outside of reading code (which is great), I like to read/know the WHY behind it. Any good resources? Tuts? Most of the Apollo or GraphQL tutorials are for React. I've been waiting for one of you Vue/Apollo experts to put something out that I can purchase. :)
Just found: https://akryum.github.io/vue-apollo/guide/#what-is-graphql
Worth mentioning these packages:
I ended up using Apollo directly, similar to this approach: https://markus.oberlehner.net/blog/combining-graphql-and-vuex/
I have been working around various approaches and tried to give a chance to using vue apollo and cache without vuex. I admit the caching woks well but one key aspect i could not solve is to be be able to use cache state in a computed property which we use a lot with vuex getters to react to change of state.
I think those comments are not 100% acurate :
This is the concept behind Apollo. Co-locate the data requirements of each component with the component itself, like you do with presentation/logic. Apollo does the rest!
The Apollo community is also moving towards apollo-link-state for local client-only data instead of relying on Redux/Vuex/etc.
while the principle of vue is to bring together presentation and logic, most of the time the logic to access to the backend is managed in separete files and stored centraly. And while apollo-link-state is not a bad tool it is far from bringing capabilities vuex provides. in sucg case we can challenge the relevance of using such a librayr compared to use apollo-client natively with vuex.
FYI also. apollo-link-state has been moved into the Apollo client core, so the idea of getting rid of Redux or Vuex has become a first class citizen. Version 2.5.0 of Apollo Client will include it and the alpha is basically ready for testing now.
Scott
@smolinari , Hi Scott thanks for the feedback, do you know how we can use the state in a computed property. a simple exemple is I am trying to create a computed property to check if a user is logged In. so i created a isLoggedIn typeDefs in my local cache and change its value from false to true when users are logging in and out. but the process to access the cache is asynchronous and the computed property does not provide the reactivity vuex getters provide. is there any easy way to do that with apollo-link-state ?
@smolinari , Hi Scott thanks for the feedback, do you know how we can use the state in a computed property. a simple exemple is I am trying to create a computed property to check if a user is logged In. so i created a isLoggedIn typeDefs in my local cache and change its value from false to true when users are logging in and out. but the process to access the cache is asynchronous and the computed property does not provide the reactivity vuex getters provide. is there any easy way to do that with apollo-link-state ?
When you use the local cache to store whether the user is logged in or not, why do you still need a computed property? Can't you just use a query defined in the apollo object and use result() or update() methods to deal with updates?
EDIT: What I mean is: if you query the local cache for the login status of the user, the property will be reactive. No need for a computed property, imho.
the difference is that you can use a computed property to bind data to the presentation layer very easily. The cache requires you to initiate a query while computed property is updating the UI everytime the value changes. I coudl not find a way to do it so far with the cache. ( but I am a beginner so it may just be ignorance) this is for my poitn of vue a greta value brought by vuex. you can associate a getter to a computed property and bind it to update the UI. every time the state of the store is changed the getter will trigger a change in the computed property which through the binding will impact the UI. so in my exemple it is very easy to show or hide components or DOM elements based on this value.
I too have been wrestling with the best combination of Vue Apollo, Vuex etc. I've a trusted pattern with Vuex but that was a rest API backend. Moving to graphQL presents an opportunity to move the store to Apollo. As with @Gerald1614 the main gap in my knowledge is implementing the equivalent of getters
I too have been wrestling with the best combination of Vue Apollo, Vuex etc. I've a trusted pattern with Vuex but that was a rest API backend. Moving to graphQL presents an opportunity to move the store to Apollo. As with @Gerald1614 the main gap in my knowledge is implementing the equivalent of getters
I am the furthest possible one can be from an expert. That being said maybe this will help: https://blog.apollographql.com/the-future-of-state-management-dd410864cae2
@Gerald1614 Hasura (Postgres only) gives you subscriptions: https://hasura.io/all-features#realtimeSubscriptions
@Gerald1614 A query defined in the apollo property, will be reactive. For example:
apollo: {
loggedIn: {
query: gql`
query {
loggedIn @client
}`
}
}
The property 'loggedIn' in the component, will be updated whenever 'loggedIn' in the cache is updated. Of course you can now use 'loggedIn' in your template and it will be reactive. But maybe I still don't get you and this is a big misunderstanding.
thanks @jdus. I tried this approach and it worked. I do not know why i focused on using a local variable to bind to the query instead of using the apollo object.
do you know how we can make the apollo object accessible in an external JS file ? i am trying to use authguard to protect some routes and need thus to access the query from an external js file.
Here is a real-life example: https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli-ui/src/router.js
Um. is that using apollo-link-state?
Scott
Use the @client directive.
@Akryum merci. the exemple was very useful. i could make it all work. I just add to modify a lot the vue-apollo.js file as the one generated by vue-CLI was not exporting apolloClient. i modified it like in your exemple and made everything work. very constructive discussion, the product is very good.
Use the @client directive.
Looking through the code, I did find it was being used. Then I was just baffled as to where link state was being imported. But I dug a bit deeper and found it. 馃榿
https://github.com/Akryum/vue-cli-plugin-apollo/blob/master/graphql-client/src/index.js
Scott
Seeing as this thread seems to have some attention at the moment, I was hoping to get some guidance on how to best implement the equivalent of vuex getters on data retrieved (and cached) via vue-apollo.
For a simple scenario, I've a graphQL query returning an array of numbers, that I need to reduce down via say a getTotal() method. I understand how a vuex getter would perform this on the state, but i'm not sure in this scenario, how best to structure and implement this.
Should I be adding these server side into the types/resolvers?
Thanks in advance!
@Akryum wrote
For example, I recently wrote a reasonably big app which doesn't need vuex.
Hi, I think this is a wonderful example what currently is missing in the current state of vue-apollo/graphql ecosystem. I find the cli-ui project is beautifully organized and written. But as someone with limited experience with graphql, this feels like the scene in the "Matrix" where Neo sees the matrix code for the first time and all in all feels very un-"vueish".
I truly believe @Akryum only sees beautiful bindings, but for me it looks like a lot of obscure code.
This is not meant as an offense. Let me explain where I'm coming from. I started to fell in love with vue after seeing the first example of it, like this:
<div id="app">{{ message }}</div>
```javascript
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
and also the sugar of using state as a single source of truth for all your components:
```javascript
// pseudo-code cobbled together from vuex.vuejs.org
new Vue({
// state
data () {
return {
count: this.$store.state.count
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.store.commit('increment')
}
}
})
// Make sure to call Vue.use(Vuex) first if using a module system
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
Even here, in this vuex example, having to use a mutation to modify count almost feels to far away from a simple state management approach, but acceptable, because it's encapsulated
I'm currently in the way of refactoring code for my project. I started out without vuex or any other kind of global state management, because as vuex states on their site, you will know when you need it.
Now for my current project, we have reached a burning desire for a global state store. My research firstly of course took me to Vuex. But we are really sick of the REST api approach, where you cobble together your state with a lot of tiny request, e.g. /api/posts/ then api/posts/1/ then api/posts/1/comments to get the full picture. So I think apollo/graphql, where you get all you need in a nice json in one request, not more not less and then you have it as a "state-like" entity to work and update it, this is really the future and the idiosyncratic way to along great with Vues philosophy.
But using vue-apollo in the current state, feels like there is an final step missing. With all the direct calls to $apollo for mutation, update, etc, it seems to me one of the strength of vue is lost: components are simple/readable enough for a junior developer or even a designer to start working with it.
Example given from the vue-cli/vue-ui link posted above
// ProjectCreate.vue
import PROJECT_CREATION from '@/graphql/project/projectCreation.gql'
import FEATURE_SET_ENABLED from '@/graphql/feature/featureSetEnabled.gql'
import PRESET_APPLY from '@/graphql/preset/presetApply.gql'
import PROJECT_CREATE from '@/graphql/project/projectCreate.gql'
import PROJECT_CANCEL_CREATION from '@/graphql/project/projectCancelCreation.gql'
...
export default {
name: 'ProjectCreate',
...
apollo: {
projectCreation: {
query: PROJECT_CREATION,
fetchPolicy: 'network-only'
}
},
...
methods: {
async selectPreset (id) {
this.formData.selectedPreset = id
if (id === '__remote__') {
this.showRemotePreset = true
return
}
await this.$apollo.mutate({
mutation: PRESET_APPLY,
variables: {
id
},
update: (store, { data: { presetApply } }) => {
store.writeQuery({ query: PROJECT_CREATION, data: { projectCreation: presetApply } })
}
})
},
I totally understand why then somebody like @beebase #7 would say, that this looks just "ridiculous".
I think we need a further abstraction, so we could use vue-apollo like just another global state store.
We would have a single store file which we can import and use in all our components instead of importing all .gql separately. So taking the example above, it should look something like this, where all the graphql minutiae are hidden away for the "enduser" so it should really look simple and readable in a component, eg:
// ProjectCreate.vue
/* syntax to access apollo store should be very simple and clean
* components should render data reactive and
* if they need to change data it should be in best case like just access a struct
* or like a simplified mutation function call
*/
...
export default {
name: 'ProjectCreate',
...
apollo: {
projectCreation: this.$state.projectCreation
},
...
methods: {
async selectPreset (id) {
...
this.$state.preset = id;
},
```javascript
// @/graphql/store
/* all the hard and "messy" stuff is implemented in this single store file
const store = new VueApollo.Store({ // inspired by Vuex.Store
state: {
projectCreation = {
query: PROJECT_CREATION,
fetchPolicy: 'network-only'
},
preset: {
get(): {
query: PRESET,
fetchPolicy: 'network-only'
},
set (id): {
return await this.$apollo.mutate({
mutation: PRESET_APPLY,
variables: {id:id},
update: (store, { data: { presetApply } }) => {
store.writeQuery({ query: PROJECT_CREATION, data: { projectCreation: presetApply } })
})
},
},
}
}
},
})
Right now I'm trying to cobble together something like this, but as you can see from my "pseudo-code", my expertise with vue-plugin system is very limited and my apollo/graphql skills are null.
My road of approach would be to use `vue-apollo/DollarApollo` as inspiration on how to get "my" $store inside of every Vue component, so it could then maybe as simple as that:
```javascript
// main.js
/* example pseudo-code how I imagine you would import it into your app */
import VueApolloStore from 'vue-apollo-store'
import store from '@/graphql/store'
Vue.use(VueApolloStore);
const app = new Vue({
el: '#app',
// provide the store using the "store" option.
// this will inject the store instance to all child components.
state: store.state,
})
But with my limited knowledge, one major roadblock already hit today is: how to get this.$apollo inside my "store"? what's best practices are there to get access to another plugin from inside a plugin?
So I think this is getting a while before I get something useful to share done.
Therefor I wanted to ask is somebody maybe already working on something like this "vue-apollo store wrapper" or even has solved it for himself and want to share it?
Would it be even possible to abstract it away like this or do the components need to have the detailed access like now? I think at least it would get people "just use" apollo/graphql, instead of worrying about minutia like "sending the same request too often" when instead vue-apollo already caches it beautifully.
Or did I miss something huge why we can't have a "simple" global store/state file without losing vue-apollo greatness/flexibility?
From all the "best practices", "vuex integration" and "apollo-link-state" questions like e.g. #384 #161 #7 and this thread which seams to pop up pretty regularly, it seems to me there is a clear need for something like this?
So if somebody with slightly more vue/apollo/graphql experiences want to take the lead and run with this, I wouldn't mind and be very grateful. Or even weight in with their experience/opinion?
Anyways I will try to truck along with this, but I don't even know yet how to make a vue-plugin, even less how to publish it to npm/github/etc so people could work together on this...
Hey @JoeSchr,
I know exactly where you are coming from. And what Guillaume has done with Vue UI is like major league advanced GraphQL (he might not think it is, but it is). So, trying to make sense of it without knowing the basics is like trying to understand Chinese sentences, when you only understand like 10 characters of the Chinese alphabet (and there are 3000). 馃槃 I hope I'm not insulting you either. No intention there for sure.
So, I'd like to give you piece of mind and help you find the basics you need.
First off, as you have noted, GraphQL is a major advancement against REST APIs, simply because the GraphQL queries, mutations and subscriptions can be co-located or "designed" on a per component basis. It allows the front-end dev to work much more quickly, because she doesn't have to put too much cognitive load into understanding the API and making it work with the UI. With GraphQL, getting and mutating the data at component level just works!
More good news is, link-state, the part of Apollo which allows for controlling state on the client, is going to be a core part of Apollo-Client as of version 2.5 (in Alpha, and Beta is coming soon!!!)
As for learning, I have two things I think you should look at, which demonstrate exactly what you are looking for within GraphQL (through Apollo Client) and can teach you the basics.
One is an example Todo app that I put together, which shows how you can use vue-apollo and Apollo-Client (with link-state) for client-side manipulation of state (without Vuex).
https://codesandbox.io/s/k3621oko23
It's simple enough, but should also get you thinking along the lines of what needs to happen to make Apollo your client-state friend. Of course, a more advanced application would even mix and match state on the client and the server. That is the real beauty of Apollo. 馃槂
The next thing I'd like to you look at is the new docs for Apollo-Client's full stack tutorial with example app. It is written with apollo-react, but the whole idea of query and mutation components is what you will be learning (which vue-apollo also offers).
https://www.apollographql.com/docs/tutorial/introduction.html
Two points on this. The example app is running off of the newest alpha version of Apollo Client (which I mentioned above) and isn't quite working 100% correctly, as there is a small issue with the refetchQueries on mutations and updating the React state (it might be a react-apollo issue too). Also, the whole bit about Zeit.co and uploading the schema to Apollo Engine you can skip.
Important is, this tutorial does get you into the basics of Apollo Client, which you need to learn. Doing the whole tutorial should take you about 2-3 hours.
I hope that helps you. Once you have these things under you belt, vue-apollo should make a good bit more sense, especially with not needing to use Vuex with your Vue application, while using Apollo Client.
It's too cool for school!!! 馃槃
Scott
Btw, I am going to be porting that Apollo-Client tutorial over to a tutorial for working with Quasar Framework (which is a Vue based Framework). The main intention of the tutorial will be teaching how to work with Quasar, but inadvertently the student will get to learn Apollo and GraphQL too. I'll be finished with the app port by the end of the month and the tutorial by the end of February. If you'd like updates on that, please join the Quasar Framework Discord Channel. 馃憤
Scott
Hi Scott,
you are right, it's major league GraphQL over there! ;)
I already found the examples you posted and read all the introductions/tutorial I found. I understand whats happening in the code of cli-ui (more or less) and I know about withClientState from apollo-link-state and how to use it.
But the point I was trying to make is not that graphql/vue-apollo is too hard to use or that I don't get it, in fact I love it.
I just think there should be one more abstraction layer between the "business logic" of vue-apollo/graphql and vue representation components, so that you or somebody else you work with can still see at one glance what this component does.
That's what I was trying to show with my example of
// ProjectCreate.vue
// simplified
...
export default {
...
apollo: {
projectCreation: this.$apolloStore.projectCreation
},
...
methods: {
selectPreset (id) {
...
this.$apolloStore.preset = id;
},
createProject() {
await this.$apolloStore.createProject(this.form);
}
The question is or better said, where I'm struggling with currently, what would be the best practice to get such an store in (every) component?
After I wrote the last post, I was also thinking about maybe using an mixin, so the store could automatically populate the apollo part with all of it's own field.
export default {
...
apollo: {
// a mixin could maybe populate this with every gql query from $apolloStore
}
Another question were I sit right now: How can I use this nice setter/getter syntax of eg. vue computed properties without reinventing the wheels?
Basically I feel like this is a good idea, but I may have not the javascript/vue-interna chops to build it, but with what I could now find out about vue-apollo works/is received by reading documentations and all the questions and issues which seem to rise again and again about best practices, that this is something really needed for vue-apollo and graphql to get traction.
Because today it's way easier and more "vue-ish" to just use Vuex/REST instead of vue-apollo. So I think maybe if vue-apollo becomes "cleaner" it would also be easier to use.
How I see it:
What's missing now is a store that works with vue-apollo or is build with vue-apollo in mind, so you don't have all the unneeded stuff you get with a e.g. vuex/apollo aproach or reinvent the wheel, because all the hard stuff like caching,clientState, etc is already down. All that is missing seems to be this "store interface", then vue-apollo would be amazingly wonderful to work with.
Something like my example above so all that gql`query` and mutation, update, etc messiness is not directly straggling around in every component but sits nice and orderly in one global store file.
Edit: Tried to make my point clearer at the end
@JoeSchr To me, one of the main advantage of Apollo is being able to collocate the data requirements of a component with itself. I also recently started using gql inside component directly instead of separate .gql files (will refactor cli-ui in the future).
what would be the best practice to get such an store in (every) component?
You can't or rather you don't need to. You create your queries and mutations per component. What you are looking for is that "connectivity" to a Flux store, when in effect, there isn't that kind of store anymore, Instead, you are offered a system with a smart cache management, which you can access via the @client directive.
So you don't have a this.$store = that kind of functionality in the component, but rather a
<template>
<ApolloQuery
:query="that"
>
<div>
<!-- do what you need to do with the data here -->
</div>
</ApolloQuery>
<!-- more Queries or Mutations or other elements here -->
</template>
<script>
import gql from 'graphql-tag'
const that = gql`
{
that @client {
id
someProp
someOtherProp
}
}
`
export default {
data () {
that: that
}
}
</script>
functionality.
And of course you'd need to create definition types and client resolvers, where needed.
This querying methodology may seem much more verbose at first, but theoretically with the Flux store, you'd need to add $store retrieval logic, some sort of caching (if needed), pagination (if needed), API calling, deciding how to retrieve local and server data, etc. etc and it all be centralized away from your component logic. That makes reasoning about your what state goes where and when fairly difficult.
All those "state concerns" I just mentioned above are "standardized" or given within the Apollo / GraphQL system's APIs. Plus you get the loading feature, network error warnings and more. The best part, the data retrieval and manipulation logic can be right next to or even in your component (as Guillaume mentioned) which is less cognitive load for devs (and yourself) when returning back to the component code later. That in itself is priceless, because it is helping you with SRP. I mean, if you have too many queries or mutations, you know your component is doing too much (like in my Todos.vue example btw!!! I think I'll change that too.....EDIT: The code now is refactored.). 馃榿
So, don't shy away from needing to write a bit more code in the component. It's a lot less (mental) work in the end with GraphQL. 馃槃
@Akryum - how about making gql global within Vue instead of having to import it in every component?
Also, I've refactored my Todo app to have the queries in the components. Would you consider the use of the options a correct way to define queries? Or is there a better way? I tried my best to declare the query in TodosList.vue as an extra named export, but being the component is already the default export and my ES6 modules foo isn't very good, I couldn't get it to work.
https://codesandbox.io/s/k3621oko23
馃槂
Scott
Hi Guys,
thx for weighting in!
@Scott:
I'm afraid I haven't made myself very clear:
What you are looking for is that "connectivity" to a Flux store, when in effect, there isn't that kind of store anymore, Instead, you are offered a system with a smart cache management, which you can access via the @client directive.
This querying methodology may seem much more verbose at first, but theoretically with the Flux store, you'd need to add $store retrieval logic, some sort of caching (if needed), pagination (if needed), API calling, deciding how to retrieve local and server data, etc. etc and it all be centralized away from your component logic.
All those "state concerns" I just mentioned above are "standardized" or given within the Apollo / GraphQL system's APIs. Plus you get the loading feature, network error warnings and more.
Yes, I know all of that goodness and that's what get's me excited about vue-apollo/graphql. BUT I'm not talking about replacing it with a store, merely of offering a different interface on top of it, which feels more "vue-ish" then the verbose way now.
"My store" interface how I imagine it would just pass-through all the apollo-goodness while on the same time offering a simpler to read experience.
// @/graphql/store
import PROJECT_CREATION from '@/graphql/project/projectCreation.gql'
const store = new VueApollo.Store({ // inspired by Vuex.Store
state: {
projectCreation = {
query: PROJECT_CREATION,
fetchPolicy: 'network-only'
},
No need to reinvent everything vue-apollo/apollo/apollo-link-state already does. Just a less verbose interface for use in components with the added plus that all the business logic is in one place.
So for example, a developer, who is really good with vue in general but doesn't know much about apollo/graphql. Imagine he now wants/needs to add something to vue-cli/vue-ui and looks at an component, he probably would be very afraid to touch anything because of fear of breaking something, because it's all very verbose and graphql specific.
If instead he would just see a few lines like this:
export default {
...
apollo: {
projectCreation: this.$apolloStore.projectCreation // immediately clear this will just be projectCreation object
},
methods: {
createProject() {
await this.$apolloStore.createProject(this.form); // immediatly clear this just adds an Project
}
}
}
They just know without any deeper intimacy of graphql that at this line just a new project is added or that they can just read from projectCreation. More generally said, in this lines a state is just fetched/changed. No need for a context switch to another language like graphql. It should be really easy for this component to later switch it to another store like vuex or use another http/REST api if so desired.
The best part, the data retrieval and manipulation logic can be right next to or even in your component (as Guillaume mentioned) which is less cognitive load for devs (and yourself) when returning back to the component code later.
That in itself is priceless, because it is helping you with SRP.
To me, one of the main advantage of Apollo is being able to collocate the data requirements of a component with itself. I also recently started using gql inside component directly instead of separate .gql files (will refactor cli-ui in the future).
That's are all valid points, I understand. I think our philosophy how a component should look and act just differ. I was looking here if maybe somebody shares my kind of philosophy and already did something in this direction or has more experience/foo with vue-plugins/apollo and wants to try create something/help with it. Maybe I will just share what I got once I got something to run, no matter how hackish it gets, so you guys can get the gist of it. Once again I'm not trying to replace vue-apollo with a store, just give add another layer interface so it feels more like a typical flux store when working with it and reads less verbose.
You can't or rather you don't need to.
I think I may just get some inspiration how vuex-apollo does it then. They must have access to vuex instances as well as apollo.
Edit: added last part, pressed post comment too soon by accident ;)
Now I understand what you are looking for. Thanks for keeping at the explanations. But, your wish for better readability/ more Vue-like code (from your perspective) will end up an extra layer of abstraction, which only means extra work and added verbosity. That's because in your example, this.$apolloStore.projectCreation still needs the queries, typDefs and resolvers to be created to get the needed data.
Instead of wanting to grab the central store and have it magically return the data needed, you should think in terms of querying and mutating that store. The queries and mutations themselves are also quite declarative and basically self documenting. Unfortunately this.$apolloStore.projectCreation isn't and instead of simplifying, such efforts will actually make reasoning about the code much more difficult, which goes against your wishes for helping new devs to the code.
If I may ask, what's not "Vue-like" with my Todo app in your opinion? Can you give me some concrete examples where you are going, "this looks really odd to me"? Maybe I can get your mental model changed, so you can see "the Apollo/ GraphQL way" is a very good methodology for state management, both on the server and the client. 馃槂
Edit: I just looked at my refactored Todo app again and to me it is totally Vue-like. It can't get any more Vue-like. The queries and mutations are within the components where they are needed, they are clear in what is wanted in terms of data and the components are all doing only one thing (aka SRP). Please do try to tell me what you don't understand or don't see as Vue-like. It doesn't get any better than that. 馃槃
Scott
Hi Scott,
sorry I didn't mean to offend by using "vue-ish"/"vue-like" or trying to be a gatekeeper.
Unfortunately
this.$apolloStore.projectCreationisn't and instead of simplifying, such efforts will actually make reasoning about the code much more difficult, which goes against your wishes for helping new devs to the code.
Maybe it wasn't the best idea of renaming it to this.$apolloStore.projectCreation, I just wanted it to be clear, that this come from my "store interface". And I'm still not really sure what projectCreation actually is, it was just the first thing I used for my example. In a real app, this line would read more like this and I can't believe that you wouldn't find this more readable:
// ViewProjects.vue
// fictional
...
export default {
...
apollo: {
allProjects: this.$state.projects
},
...
}
This file which includes, while hard to read, all the flexibility and goodness of graphql/apollo is squared away and usually almost nobody working on just the vue component needs to looked at it:
// ./store/apollo/StoreInterface.js
import ALL_PROJECTS from '@/graphql/project/projectsAll.gql'
const store = new VueApollo.Store({ // inspired by Vuex.Store
state: {
projects: {
get() {
query: ALL_PROJECTS
fetchPolicy: 'network-only'
},
set() { // maybe do some mutation to add a project
}
}
}
Edit: I just looked at my refactored Todo app and to me it is totally Vue-like. It can't get any more Vue-like. The queries and mutations are within the components where they are needed, they are clear what is wanted and the components are all doing only one thing (aka SRP). Please try to tell me what you don't understand. It doesn't get any better than that. 馃槃
As I explained in my first post, what I love most about Vue is the simple and powerful way, where you have one state, a global source of truth from which everything else then reacts and flows. When you then change the state, you do it in an "atomic" way. Atomic not in the sense as used with mutex, but like more a single line of code which you can grasp with one look. Also you can glance over a Vue component in a few screens an get a pretty good idea what's happening. That's what I also find most valuable about SRP approach but of course isn't technically what SRP is about and totally misapprehension of it. So maybe it's better to say I don't care about SRP that much.
So for example in your codesandbox.io, I like how you do this:
// TodoInput.vue
data() {
return {
todo: '',
addTodo: ADD_TODO
}
It's one line of code and I don't really need to know, that there is some graphql stuff going on. it could also be a vuex store or something completely else that manages the state. it's abstracted away, all I see is how to modify my state.
I don't care so much that the state gets modified with a SRP approach, so I would put the definition of APP_TODO in a helper/store file, with all the others I would eventually need. this is what I'm trying to do.
On the other kind, while glancing over I didn't care much for this:
// TodosList.vue
queries: {
getTodos: gql`
{
todos @client {
id
todo
completed
}
}
`
},
}
but then I saw that it was used exactly like I explained above here:
// TodosList.vue
data() {
return {
todos: this.$options.queries.getTodos
}
},
So my mind immediately got caught by the verbosity and strangeness of having gql code there, inside the component. as much as having all this nested mutation(){... update(){...}) stuff, which is just "ridiculously verbose" when you don't expect it. If this would be squared away in an helper file or "store like interface", for me it would feel more "vue-ish"
Instead of wanting to grab the central store and have it magically return the data needed, you should think in terms of querying and mutating that store.
True, but I should have to switch, to say it hyperbolic, to another language (graphql) just to fetch/mutate my store/state.
The queries and mutations themselves are also quite declarative and basically self documenting.
I think that's were I beg to differ. I believe it gets a lot easier the more you learn graphql, then it happens almost subconscious. But it still believe, managing the state inside a vue component should almost as easy and concise as doing it with a native JS struct/object.
extra layer of abstraction, which only means extra work and added verbosity. That's because in your example, this.$apolloStore.projectCreation still needs the queries, typDefs and resolvers to be created to get the needed data.
Totally true. Also in fact, really fine, I want to be able to create all the queries, typDefs and resolver, to be able to wield this power. I just feel, it shouldn't happen onthespot inside a SFC vue component, were it messes up the awesome simplicity of vue's syntax.
For me that's more worth then technical correct SRP. because in my mind it's still SRP as long as I see where the component is modifying the state. If this happens via graphql or websockets or localStorage or http request or vuex or vue-stash, that's just an implementation detail. Which in my mind a vue component should not have to care about.
Like I said, I think you two guys just have a different philosophy and approach, which is totally fine. I was just looking for someone which maybe wants to help to make it more concise or most ideally already has a best practice how to square everything away in a helper file or store like interface without reinventing the wheel like you said.
maybe I will try to use your example as an simple starting point to try to show you what I mean. It feels more approachable then having to clean up my own project first before I can try it out. Thank you for that great jumping point!
got caught by the verbosity and strangeness of having gql code there
Aha! This mindset needs to change, because the query language is a core strength of GraphQL. Any query defines exactly what is expected to be retrieved from the "store" (actually the Apollo cache). If you want to, you can put your queries in a .gql file and import them. Then you have almost what you want. But, I'd not put any other layer of abstraction on top of the queries. They are a necessity and a strength. You need to embrace them. 馃槃
were it messes up the awesome simplicity of vue's syntax
Well, I don't think the queries or mutations mess up the Vue syntax. I think it enhances it, a lot! Think about it this way. You are a developer and you want to understand what pieces of state are being brought into the component. Let's say, it's a blog post. And in a blog post, you could just have
```javascript
data () {
return {
blogPost: getSomeBlogPost()
}
...
That's what you are looking for right? But, how does one know what is being delivered, what props are there to be "consumed"? The dev would need to go looking at that method and what it does.
When the gql query is in the component, the dev knows exactly what data is being "pulled" into the component for instance.
Like this:
```javascript
data () {
return {
blogPost: gql`
query singlePost($slug: String!) {
post: Post(slug: $slug) {
id
slug
title
description
image {
url
}
}
}
`
}
... // the rest of the component
Now imagine that dev is needing to make a change (and regarding SRP), with the gql query (or mutation) in the component, she would simply go to the component and update it with the new field or fields needed and.... Boom! Those fields are ready to use and the dev just adds the necessary interpolation fields in the template code for rendering. With the function, the dev would have to go do some change somewhere else in code she might not understand. That breaks SRP. 馃槃
Now think about the dev coming to this component and just wanting to understand it. She will see right off the bat which fields are coming in from the server. And, with the @client directive, she would also directly see what is being managed on the client as client side state too.
How awesome is that?
Thank you for that great jumping point!
No problem. Just don't spend too much time with it trying to fight the flow. 馃榿
Scott
Hi Scott,
it's true that by getting rid of the query, you have to go hunt for the fields and specifications. that's one downside of it. great thing, with using a global store, there is only one file where it could be and you already know how it's called.
But at the same time, when using vuex, I also don't have my API calls where I specify which arguments get passed directly in my SFC. This is why I feel, like I wrote in my first post, that the "final step" of vue-apollo is missing. It feels too barebones to use it in SFC directly.
I just modified your example, maybe it's clearer now: https://codesandbox.io/s/k2jyq0np0o
You are using ApolloQuery and ApolloMutation mostly, which works beautifully. The only thing I modified using them, was to bring in the query strings via my "store". But could also just see of another way how you access constants. at least they are not messing up the sfc anymore
But then I saw in Todo.vue, that you strikethrough your todo item once it's completed. BUT you do it via a @click action that basically just sets a intern property which then is used :style="`textDecoration:${labelStyle}`". That's an example of not being "vue-ish" for me.
For me the style of the component has to flow from the data directly. If a todo is completed, there should be no need for an action which then changes the style (dirty, having a separate ui style state) but the style should flow from the state (also it wouldn't work once you reload, because it's only "saved" in DOM).
So I created an computed property which returns the strikethrough depending if todo.completed is true.
computed: {
// a computed getter
labelStyle: function() {
// `this` points to the vm instance
return this.todo.completed ? "line-through" : "none";
}
The click action now directly changes the state of the todo. It does this via the store and the component writer doesn't need to use any gql for it.
methods: {
toggleTodo() {
$state.todo.complete(this.todo,
this.$apollo);
}
Of course like I commented it would be even better if it would work directly via this.todo.completed = !this.todo.completed, but I fear, that's even farther away.
But the next thing I will try to work on is to make this possible:
methods: {
toggleTodo() {
this.todo.completed = !this.todo.completed;
this.$state.todo.update($this.todo);
}
Ideally only updating fields which are changed. Or hopefully vue-apollo is already keeping track of this for me...
Obviously $state should not be needed to be imported in every file, just be accessible via this.$state. and you also shouldn't need to pass this.$apollo. I have to get my vue-plugin foo up to notch to make it possible.
great thing, with using a global store, there is only one file where it could be and you already know how it's called.
Um, in a big application, this is NOT a good thing to have. Anything "god-like" is basically a bad coding pattern.
That's an example of not being "vue-ish" for me.
I actually just changed it to a computed property myself too, because the function caused a bug. If you selected the filter for active tasks, and went back to all tasks, the strike-through was missing on the completed tasks. Oops! 馃榿
And, that code has nothing to do with Apollo usage really. 馃槂
I see where you are heading and it's also doable, i.e. creating a store for the queries and mutations. But, it's counter-intuitive to me. I mean, Guillaume was doing something similar and has decided to change it to local queries and mutations. I'll bet it's because the code is then easier to reason about and that is what Vue is all about too. 馃憤 You just have to agree it's the better way to make the SFC comprehensible at first sight. 馃槈
Scott
Hi Scott,
yeah, like I already said, it comes down to preference and philosophy and I don't want to convert you individually. If you don't want it or don't like it, you don't need to use it.
I just was looking for somebody similar minded, which is maybe already thinking alongside an similar approach.. Which is not so far fetched, if you look at all the questions which pops up around this.
Um, in a big application, this is NOT a good thing to have. Anything "god-like" is basically a bad coding pattern.
But as far as I understand, Vuex also does something like it, were all the "business logic" is at the same place with all the other actions, mutations, etc. And not inside the SFCs...? Maybe when working more with it, I will also get sick of it like @Guillaume. We will see.
I actually just changed it to a computed property myself too, because the function caused a bug. If you selected the filter for active tasks, and went back to all tasks, the strike-through was missing on the completed tasks. Oops!
Yeah, because in Vue, unlike eg. jQuery, you don't change the DOM/style directly, you change the data/state and let the representation handle it. Every time you change something, it gets then computed from the single source of truth, functional programming style. I love it, but sometimes hard to wrap your head around :-D
And, that code has nothing to do with Apollo usage really. 馃槂
No biggie, I just mentioned it just because you ask what I don't find "vue-ish" about it. Also while refactoring TodosList.vue so it's not using ApolloQuery, I saw your allDone and visibleTodos methods. I think to be more "vue-is" they should be computed too, because you already have all the data in this.todos and it get's then filtered automatically when somebody changes in the filterBar aka it gets "computed". that's what I mean with following the flow of the state.
computed: {
allDone() {
if (this.todos.length === 0) return false;
return !this.todos.some(function(todo) {
return !todo.completed;
});
},
visibleTodos() {
switch (this.listFilter) {
case "SHOW_ALL":
return this.todos;
case "SHOW_COMPLETED":
return this.todos.filter(t => t.completed);
case "SHOW_ACTIVE":
return this.todos.filter(t => !t.completed);
default:
throw new Error("Unknown filter: " + this.listFilter);
}
},
<li v-for="todo in visibleTodos" :key="todo.id"><Todo :todo="todo" /></li>
<p v-if="allDone">You did everything, awesome!</p>
But as far as I understand, Vuex also does something like it, were all the "business logic" is at the same place with all the other actions, mutations, etc.
Sort of. The Vuex store can and should be broken up into modules and can be namespaced.
The changes to computed properties are good ones. I'll update my sandbox.
Scott
Sort of. The Vuex store can and should be broken up into modules and can be namespaced.
Of course, this has to be possible with "vue-apollo-store" in the end as well. Like I said, using it should feel like just another flux store, the graphql goodness is just squared away at the store area/namespace/file/folder and not inside the SFC.
But this feels huge for somebody like me which has never written a vue-plugin. So if anybody else with more experience in the vue ecosystem feels interested in taking this on, I won't be mad! Just tell me about it! ;)
Or if somebody want to give me pointers for starters I would be really grateful.
I have 2 major roadblock, where I'm not sure about whats best practices or where to even start:
First
I have to get my own $state inside every component so I can use it like above without importing in every file. I know I can look at apolloDollar function in vue-apollo, but I don't know what is chaff for my usecase and what's really needed
Second
Also what's the best practices to initialize this $store, so I can use $apollo instance the same way as this.$apollo is used now from every component. This seems to be similar on how Vuex get's it's state and mutation defined. Question is, what property from vue-apollo do I need to pass along and how to access it?
Third
This is farther down the line, but maybe I have to keep in the back of the head to take the right approach now.
I may be really cool to make something similar to how Vuex initializes it's state.
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
So it would be really cool, if this state then could be automatically mixed in with
apollo: {
count: gql
}
by my plugin?
Question is, is mixin the right aproach? Can I even make a mixin for another plugin? If yes, can I just populate vue-apollo's apollo:{} with my own gql template literals ?
The changes to computed properties don't work, because the ApolloQuery component seems to have a different scope. So, I have to use methods to get the todos into the scope of the component to manipulate the data. The todos data property isn't the todos themselves (so maybe bad naming), but rather just a holder for the query. Theoretically, I don't need the todos data prop. I'm going to look more into the scoping of the ApolloQuery component.
And you must admit, even with a simple app like this, the amount of additional code you are adding is a lot and it's making what is fairly simple, a lot more complicated and.....all that just so components look a bit "cleaner"? 馃槈
Scott
How can you ask for best practices, when what you are wanting to do has never been done?
To #2: The Apollo Client (the store) is initialized via the defaults property (see main.js). So, any data being controlled on the client through Apollo Client is initialized with the defaults property.
Scott
The changes to computed properties doesn't work, because the ApolloQuery component seems to have a different scope. So, I have to use methods to get the todos into the scope of the component to manipulate the data. The
todosdata property isn't the todos themselves (so maybe bad naming), but rather just a holder for the query. Theoretically, I don't need the todos data prop. I'm going to look more into the scoping of the ApolloQuery component.
Hmm, it just works at my end and I'm still using the same gql I got from your original example. Maybe take a look at it?
And you must admit, even with a simple app like this, the amount of additional code you are adding is a lot and it's making what is fairly simple, a lot more complicated and.....all that just so components look a bit "cleaner". 馃槈
Of course I'm adding a lot of stuff now, because this "vue-apollo-store" doesn't exist yet, the whole reason I write all this.
In the end, once this exist, you should be able to just import & use it like any another plugin with 1-2 lines and then use it like this but with all the graphql/apollo instead of vuex/axios. Maybe like this?
const store = new Apollo.Store({
state: {
todos: ALL_TODOS // is just another gql import from a ".gql" like right now
},
mutations: {
addTodo (todo) {
this.$apollo.mutation(ADD_TODO,{todo});
}
}
})
Or it will be a mixin? But it shouldn't be anymore boilerplate like right now, just all in one place (or seperate namespaces if you want)
How can you ask for best practices, when what you are wanting to do has never been done?
I mean more like best practices how to interact with other plugins, how to initalize, like I wrote above
But I feel like I repeat myself now. Like I said, you don't have to use it, if you don't like the idea.
But if somebody else wants to work on it or give me some pointers, please step forward! :-D
To #2: The Apollo Client (the store) is initialized via the defaults property (see main.js). So, any data being controlled on the client through Apollo Client is initialized with the defaults property.
Yeah I saw that. But my question is more, how to write your own plugin, which does something similar. So working from this, it should look something like this for the "enduser"
// store.js
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import VueApolloStore from 'vue-apollo-store'
Vue.use(VueApollo)
Vue.use(VueApolloStore)
const state = {
...
}
export default new VueApolloStore({
state
})
// main.js
import apolloStore from "store.js"
const client = new ApolloClient({
clientState: {
defaults,
resolvers,
typeDefs
},
uri: ''
})
const apolloProvider = new VueApollo({
defaultClient: client
})
apolloStore.setProvider({
defaultProvider: apolloProvider
})
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
apolloStore,
render: h => h(App)
})
Question is, how and why does new Vue({}) now work with what you pass along here. What's minimal necessary if you take a look at eg. vue-apollo/dollarApollo.js to make it work like that. Also how can I make so vue-apollo is still working as intended, just with my store plugin on top it.
That's what I'm stuck with right now and where I hoped to get some pointers to accelerate this process. But as always, you never can jumpstart reading the docs and browsing the source ;)
Hmm, it just works at my end
Yeah, because you took out the <ApolloQuery> component. 馃槃
But it shouldn't be anymore boilerplate like right now
There is no boilerplate right now. Your attempts are only adding more abstraction and verbosity to an already finished abstraction, and there is no way you can simplify the Apollo Client / GraphQL API, because you still have to fulfill it to make the intended data retrieval and manipulation work.
once this exist, you should be able to just import & use it like any another plugin with 1-2 lines
It seems to me, you are trying to fit the mental model of Flux architecture into Apollo Client's non-Flux system for state management. You are doing exactly what the Apollo team are trying to overcome, needing two state management systems on the client.
you don't have to use it, if you don't like the idea.
Yeah. The idea is a waste of time and I'm sorry I can't get you to change your mind about it.
Scott
This issue is too long to be useful for someone trying to figure out how to deal with state in a vue + graphql app. So it would be better to maintain a section in the official docs related to this topic or even better docs about vue + graphql in general covering authentication and authorization
@lobosan
So it would better to maintain a section in the official docs related to this issue or vue + graphql in general.
Although the vue-apollo docs (I am assuming that is what you meant with official docs?) are a bit limited and could go into more detail with examples and explanations, there is no topic to cover from this thread in the docs.
Or put another way, you don't need Vuex with Apollo Client anymore to run Vue with a GraphQL server. So, there are no best practices when using the two together. It's simply no longer necessary.
If you understand Apollo Client along with Apollo link state, then the vue-apollo docs should get you rolling with Vue and Apollo (GraphQL).
Scott
It seems to me, you are trying to fit the mental model of Flux architecture into Apollo Client's non-Flux system for state management. You are doing exactly what the Apollo team are trying to overcome, needing two state management systems on the client.
I don't understand what you mean? Why would that make it two state management systems? How I imagine it and showed above, it's more like another interface to access the same state management which gets handled by vue-apollo. It's true that it adds more abstraction, but it doesn't double it, because it still refrencing, reading, mutating the same graphql/apollo-client/apollo-state-link?
it's more like another interface to access the same state management which gets handled by vue-apollo
Correct.
Why would that make it two state management systems?
Because I'm fairly certain at some point, when we have a large application, your extra layer of abstraction will also need some sort of extra management to make sense of it all. It might not be a state management system, correct, but it will be some sort of (extra/ unnecessary) system.
You are also moving the logic for reasoning about state and data control away from the point where it should be controlled.
And lastly, you are trying to make a declarative system into a different type of declarative system.
This may sound harsh, and I don't want to discourage you or hurt any feelings, but you are solving a problem that is only between your own ears. 馃槉
Scott
I'm going to add one more thing to hopefully change your mind @JoeSchr . The query and mutation calls in the components have the ability to "define the shape of the returned result data". In other words, if the developer can see what can be returned from either a GraphQL endpoint or a client resolver, then she can ask (query) for only specific fields of that data. And again, this is another one of the great advantages to GraphQL. It is also that extra verbosity you have an issue with in the component. The query in the component isn't THE query, it's A query for data, but in the specific form the component needs it in (and why it may seem verbose). The Todo app doesn't demonstrate this response structure control, unfortunately.
So put another way, your intentions of redefining queries into simpler commands is obscuring this ability and flexibility to define the response structure. For every query needed with a new response structure, and in a big app it could get quite intense, you'd have to come up with a new wrapping function, or even a new DSL, and doing all that is just so you can be at ease with what you are seeing? I get it too. I hate JSX and this is sort of like JSX. But, you are a smart guy. Dream a little and imagine the consequences of your intentions. Please. 馃槈
Btw, I've updated my Todo app to delete completed tasks too. 馃槃
https://codesandbox.io/s/k3621oko23
@Akryum - if you consider it worthy (and I'd really like your opinion on it), I'd like to add it to the examples in the docs for client side state. My version of the app was originally Sara Viera's example app. I've just enhanced it some. 馃槃
Ok? 馃檹
Scott
@smolinari Why not! :smile_cat:
For every query needed with a new response structure, and in a big app it could get quite intense, you'd have to come up with a new wrapping function, or even a new DSL, and doing all that is just so you can be at ease with what you are seeing?
A valid point, but also just another tradeoff
I think one has to take this into considerations with the kind of application they builds. But I would rather have there be a choice than there be no choice at all.
For the kind of application I'm doing right now, only querying a subset of the whole dataset is not a usecase that happens often (if at all). On the other hand, the components are already very big and unwieldly, so I really hestitate to make it even more verbose.
You probably will say, that's a sign it needs to be splitted up, but the reason is not to many responsiblities, rather just because there are a lot of inputs that needed to be filled out, imagine like a big tax form sheet.
Further on I have gotten really sick of having all my data queries sprawled all around the place, which really makes bug hunting an issue. This firstly led me to search for a vuex like global state management, where you have a better handling on state management changes via dedicated mutations/actions.
That's all more important to the kind of application I'm working on, then having granular control over how much I fetch, because I mostly fetch everything.
So my plan right now is to replace all the axios calls with apollo (inside each) component and decide from there if this is good enough or if it still causes the same issues and move some or most into a store like entity.
Btw, I've updated my Todo app to delete completed tasks too. 馃槃
Nice, I was thinking of adding that along as well, to get a better feeling for it. But then the strikethrough caught my eye and I decided this was a smaller challenge to try it out
the components are already very big and unwieldly
You probably will say, that's a sign it needs to be splitted up,
You are correct! :smile:
Just to show you how you can "split it up", check out this fiddle.
https://jsfiddle.net/smolinari/65meb0px/
It's using Quasar Framework.
This form is only three questions and is a survey, but the whole concept can be scaled to any size form. Also, currently the data is being formed and given in the component. Imagine the form being built automatically, where each input field type does its own mutation. That is incredibly powerful.
which really makes bug hunting an issue.
Is it a REST API you are using? If yes, this is where GraphQL(done right) is going to blow it out of the water. Front-end developer efficiency both in coding and troubleshooting was one of the reasons why Facebook came up with GraphQL, believe it or not. :smiley:
Scott
@Akryum - PR is up! :+1:
Scott
I spent months battling with Vue Apollo (via Nuxt Apollo) and trying to get the Apollo queries/subscriptions to actually be reactive like a Vuex store would be, and all the verbose code you need to write to update the local state after each mutation. I read this thread and I can see that a lot of people feel the same way as I do.
I decided to use VuexORM to handle all my data, it took me a few hours to get setup and define my models, and I have to say it is 10x better than using the Apollo Local state. Yes you end up with "two sources of truth" in that you have the Apollo cache/local state, and Vuex, but if one of those sources of truth is a pain in the ass to use, then I'm happy to just ignore it!
If anyone is looking for a better way to handle complicated data in their Vue or Nuxt app, this will probably make you life easy!
https://github.com/vuex-orm/vuex-orm/issues/651#issuecomment-642224359
I was also thinking of using vuex orm as a local "database" and only using graphql for fetching data from the server that gets normalised into vuex relational tables. I believe there's even a graphql plugin for vuex orm. As tempting as graphql looks, It still feels scary using it with local relational database structures
I'm not sure what the problem might be which you are having with reactivity. @drewbaker If you update the cache via @client, then the queries that are involved should update too (ie. are reactive).
Was it mainly subscriptions being an issue by chance?
I can't disagree that the code you need to write to update the cache can get a bit verbose. I was thinking it needs an abstraction like an ORM too and then it would be awesome and most definitely a simplification from having two sources of truth and more importantly, keeping them in sync. I feel using Vuex at all is defeating the whole purpose of a GraphQL client with Vue. You might as well stick with REST. :)
It still feels scary using it with local relational database structures
You haven't found the GraphQL paradigm yet. Keep plugin at it! 馃榿
Shameless plug (pun not intended): Have a look at an article I wrote on the subject. It will either confuse you more or get GraphQL to click for you.
Scott
Thanks Scott. I鈥檝e since replaced Nuxt Apollo with https://www.npmjs.com/package/nuxt-graphql-request and VuexORM (on larger projects) and it works fantastic.
Most helpful comment
Yeah I'm eager to use apollo-client 2.0, it has so really nice features.
To answer your question from my experience @alidcastano, there is no problem with using vuex + apollo. It just depends on how you use them.
If your application uses a REST api, the Gitlab approach is very nice. You query your API, and store data in any format you wish in your store for further use in your application, vuex is not only used as a local app state manager but can also be used as a cache manager.
Apollo is made to interact with your graph API. You send a query, the result is normalized by stored in apollo's redux store, is a similar way the Gitlab approach uses vuex for.
So apollo (vue-apollo for easier integration) is the tool of choice to interact with your API data, locally and remotely. But you still need a tool to manage your local application state. Sure, components have state and you can pass is down to children components. But sometimes you need transversal data. The is where vuex comes in.
For instance you can have a currently selected item id (that comes from apollo) stored in vuex, so that you can request this item via apollo in different components. By default, if a query was already fetched, apollo will use the redux store instead of re-querying it, so don't hesitate to do the same query multiple times.
TL;DR: Use vue-apollo to interact with your graph API. Use vuex to manage your local app global state. But you shouldn't duplicate data between vuex and apollo.