Vue-apollo: does vue-apollo work with vuex?

Created on 4 Oct 2016  ·  115Comments  ·  Source: vuejs/vue-apollo

Can / should vue-apollo be used with vuex?
If yes then could you please show examples?

Most helpful comment

We could either do a vuex plugin or a vuex module I think.

All 115 comments

This is something that would need to be integrated into apollo-client core. What is the VueX API like, especially for integrations? For Redux we ship a special reducer and middleware, does VueX have similar integration points?

Vue, Vuex, Apollo, and GraphQL are new technologies for me so I hesitate to comment on specifics. My investigations so far lead me to believe this combo may be productive. The Vuex API is at https://vuex.vuejs.org/en/api.html . I'm hoping these can work together.

We could either do a vuex plugin or a vuex module I think.

a plugin would be great for vuex. I have another question though
can i use the apollo store for storing client generated data that doesn't require to sync with server? if yes how would i go about it?

Usually people do that by using Redux with Apollo Client. So part of your store is managed by AC (the server stuff) and the rest with regular Redux.

So integrating with VueX would mean that AC would manage the server-side data, and you manage the rest as usual.

I have never used Redux in vue, but vuex. at the moment my app is ok with managing local stuff in components but if there i a chance of vuex plugin for vue-apollo i will wait?
another option is to use bouth vuex and apollo client which is a bit of overkill in my view

another option is to use bouth vuex and apollo client which is a bit of overkill in my view

Why? It doesn't seem like a bad idea to me.

Any news on this? I'd be happy to help.

@Akryum, @stubailo This vuex-apollo thing is really bugging me and as I can see I'm not alone :). Would you be so kind and help us find a proper solution for vuex? I think/hope you are the right people to ask. It would also be great to join forces and have this topic described at dev.apollodata.com. Thanks.

After reviewing vue-apollo, apollo client, and vuex I think there may be an easy way to enhance vue-apollo to allow for vuex support. Currently vue-apollo updates the local state. In vuex the store is modified by committing a mutation.

What if a new option were added to vue-apollo to bypass the local state update and instead call a user supplied function?

Let's call the new option "apply" (or whatever makes more sense). The following updated applyData should be enough to allow support for vuex (or something else):

function applyData(data) {
  loadingDone();

  if (typeof options.apply === 'function') {
    options.apply.call(vm, data);
  } else if (typeof options.update === 'function') {
    vm[key] = options.update.call(vm, data);
  } else if (data[key] === undefined) {
    console.error(`Missing ${key} attribute on result`, data);
  } else {
    vm[key] = data[key];
  }

  if (typeof options.result === 'function') {
    options.result.call(vm, data);
  }
}

What do you think?

@davenport Please post some example code to demonstrate how this would look in a Todo (or Counter) or similar simple example with Vuex. Cheers!

@DougDavenport As I understand your example, this would work for incoming data to be fed into the store and thus force a new VDOM render phase.

methods: mapActions(...actions), // where actions includes: increment(counter)
apollo: {
  // Query with parameters
  ping: {
    query: ...,
    ...
    apply: (data) => {
      // emit as a Vuex action, that is subscribed to //
      // action then turned into a store mutation on local Vuex store...
      this.increment(data)
    }
  }

Would it make sense to go the other way? ie. for local mutations on store to also potentially cause a side effect of a graphQL mutation? I think it should only be unidirectional, as you would otherwise get into a lot of sync problems, just like with two-way data binding, ie. database/state sync in general. So this truly looks like an elegant flexible solution :)

Note: I'm very new to this, so syntax/understanding might be totally off the tracks!

while we are using the redux store under the hood with apollo as I understand, is it possible to access and set data to it in vue??

Hi I'm just posting here to say +1 - I'm also very interested in having vuex support.

I suggest you also have a look at vue-rx with apollo
Awesome!

@MikeLeonard
Isn't it enough? Just pass your apollo client to the root store, and then:

  actions: {
    $GET_PROJECTS: ({ commit, state, rootState }) => {
      return rootState.apollo.watchQuery({
        query: gql`
          query projectsList{
            projects {
              _id
              name
            }
          }
        `,
      }).subscribe({
        next: (resulut) => {
          const projects = resulut.data.projects || [];
          commit('SET_PROJECTS', projects)
        },
        error: (error, done) => {
          console.log('there was an error sending the query', error);
        }
      });
    },
}

Of course, externalise these functions. Otherwise your actions hash will not scale and quickly become an entangled jungle! :P

On another note, we need a system to avoid the "two-way sync" problem. Ie. the above example is for mutating local AND external store. Then you might also have listeners to external store, that contain changes from other users (and your self). So I guess you need to ensure you don't infinitely loop... ie, you need a commit hash or sth to identify mutations you have performed before and skip them? Your thoughts...

@kristianmandrup , @ykshev
As I was trying to think through an example of using vuex and vue-apollo together, it seemed to make more sense to just use the apollo client directly with vuex. Is that what you are suggesting?

@DougDavenport Yes!

Some mind experiments for how it could look.

_Mutations_

// apollo/mutations.js

export const addTag = (state, newTag) => {
  this.$apollo.mutate({
    // Query
    mutation: gql`mutation ($label: String!) {
      addTag(label: $label) {
        id
        label
      }
    }`,
    // Parameters
    variables: {
      label: newTag,
    },
    // Update the cache with the result
    // 'tagList' is the name of the query declared before
    // that will be updated with the optimistic response
    // and the result of the mutation
    updateQueries: {
      tagList: (previousQueryResult, { mutationResult }) => {
        // We incorporate any received result (either optimistic or real)
        // into the 'tagList' query we set up earlier
        return {
          tags: [...previousQueryResult.tags, mutationResult.data.addTag],
        };
      },
    },
    // Optimistic UI
    // Will be treated as a 'fake' result as soon as the request is made
    // so that the UI can react quickly and the user be happy
    optimisticResponse: {
      __typename: 'Mutation',
      addTag: {
        __typename: 'Tag',
        id: -1,
        label: newTag,
      },
    },
  })
}

_Queries_

// apollo/subscriptions.js
export const projectList = rootState.apollo.watchQuery({
    query: gql`
      query projectsList{
        projects {
          _id
          name
        }
      }
    `,
  })

_Subscription services_

import { * as subscriptions } from './apollo/subscriptions'
import store from './store'

export getProject = () => {
  subscriptions.projectList.subscribe({
    next: (result) => {
      const projects = result.data.projects || [];
      store.dispatch('setProjects', projects)
    },
    error: (error, done) => {
      console.log('there was an error sending the query', error);
    }
  });
}

_Actions_

// apollo/actions.js

import { * as mutations } from './apollo/mutations'

export const addTag = ({ commit, state, rootState }) {
    mutations.addTag(state.newTag)
    .then((data) => {
        // commit to VueX store with confirmation result from server
        commit('addTag', data)
    }).catch((error) => {
      // Error
      console.error(error);

      // Restore the initial newTag??
      commit('clearTag')
    });
  }

const setProjects = ({ commit, state, rootState }) => {
  commit('setProjects', state.projects)
}

_Actions_

// actions.js

import { addTag, getProject } from './apollo/actions'

export const actions = {
  increment ({commit}) {
    commit('increment')
  },
  addTag,
  getProject
}

_Mutations_

// mutations.js

export const mutations = {
  increment (state, n) {
    // mutate state
    state.count = state.count + n
  },
  setProjects: (state, projects) {
    state.projects = projects
  },
  addTag: (state, newTag) {
    state.tags.push(newTag)
  },
  clearTag: (state) {
    state.newTags = ''
  }
}

_State_

// state.js
const state = {
  count: 1,
  tags: [],
  newTag: '',
  projects: []
}

Then export actions, mutations and state from ./config.js

import { actions, mutations, state } from './config'

const store = new Vuex.Store({
  state,
  mutations,
  actions
  }
})

As the example above demonstrates, it would be best to follow the @ykshev architecture proposal of putting the logic in an action, so mutations are kept "clean".

Note: I'm still a novice with Vue2, Apollo and VueX. I'm I on the right track here!?

However, perhaps VueX should be made better suited to subscription models? What do other socket solutions integrate with VueX? I think there should be a separate apollo subscription Service, which when it receives data performs an action on VueX, which is committed.

I have no idea how to handle the optimisticResponse with Vuex. Any ideas? Somehow when we do a store.dispatch from the component we need to "catch it" there and do a fake UI update until we get a full cycle roundtrip with state update and re-render ?

as far as I understand, the action can return a promise, so if the optimisticResponse could resolve the promise it's all good :)

ah well, the optimisticResponse it just a fake response which would then be fed into the VueX store/state.

{
        __typename: 'Mutation',
        submitComment: {
          __typename: 'Comment',
          // Note that we can access the props of the container at `ownProps`
          postedBy: ownProps.currentUser,
          createdAt: +new Date,
          content: commentContent,
        },

The __typename is apollo specific metadata, which I guess could be ignored.

// from a component action handler method (f.ex activated by button 'add' click) 
store.dispatch('addTag', newTag)
  .then((fakeResponse) -> {
  this.tags = fakeResponse.tags
  })
  .catch(err => this.handleError(err))

Sweet :)

Why are trying to have two sore management systems redux (used by apollo under the hood) and puting vuex on top of it. Why can't we access the internal redux store for local state? am I missing something here??
See issue here

Good question that I have asked myself. It may be possible but vuex is a reactive store that integrates with vue components.

https://vuex.vuejs.org/en/getting-started.html

At the center of every Vuex application is the store. A "store" is basically a container that holds your application state. There are two things that makes a Vuex store different from a plain global object:

  1. Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state changes.
  2. You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations. This ensures every state change leaves a track-able record, and enables tooling that helps us better understand our applications.

I think the problem with what you are suggesting is described by:

https://vuex.vuejs.org/en/intro.html

I've been sort of fanning the fire on these debates and for the push to Vuex and I will admit, I've done too little otherwise and I apologize for that.

It seems to me though, especially with Vue 2.0 now using a virtual DOM, and in a sense working more like React, that a direct integration of Vue and Apollo (like with what we have here) is going to work just fine. In other words, we don't need Vuex anymore, as its flux type of system was built mainly for the type of reactivity Vue 1.0 offered. Remember, my fanning the need for Vuex integration was being done in the Vue 1.0 days. :smile:

I welcome the more conceptual discussion on this. Where are the cutting points between Apollo and Vue, which aren't possibly working quite right? Or is everything ok, as I suspect it is?

Scott

Has anyone dug any further on this? Or has everyone been finding the Vue-Apollo solution appropriate?

Scott

HI @smolinari I'm using vue-apollo without any issues. For vuex I am using the npm apollo client directly - which works as well very good so far.

@dohomi - are you using both in the same project? Or in different projects?

Can you share some example code on how you are using Vuex with the apollo client directly?

Scott

I use both in the same project.. here some example code to login:

mixins/vueMixins.js

const fetchUser = {
    beforeCreate () {
        const store = this.$store;
        return new Promise((resolve, reject) => {
            if (store.state.authUser) {
                resolve();
            } else {
                return store
                    .dispatch('getUser')
                    .catch(e => console.log(e));
            }
        });
    }
};

export default {
    fetchUser
};

store/actions.js

/**
 *
 * @param {String} commit
 * @returns {Promise}
 */
export const getUser = ({commit}) => new Promise((resolve, reject) => {
    apolloClient
        .query({
            query: user,
            forceFetch: true
        })
        .then(({data}) => {
            const r = data && data.user;
            commit(types.SET_USER, r || null);
            return resolve(r);
        })
        .catch(er => reject(er));
});

store/index.js

import * as actions from './actions';

const store = new Vuex.Store({

    state: {
        authUser: null
    },

    mutations: {
        [types.SET_USER]: (state, user) => {
            state.authUser = user;
        }
    },
    actions
});

export default store;

plugins/apolloClient.js

import Vue from 'vue';
import VueApollo from 'vue-apollo';
import ApolloClient, {createNetworkInterface} from 'apollo-client';

const apolloClient = new ApolloClient({
    networkInterface: createNetworkInterface({
        uri: apolloUri,
        transportBatching: true
    }),
    dataIdFromObject: r => r.id
});

Vue.use(VueApollo, {apolloClient});

Inside of *.vue files I just use the apollo for queries or this.$apollo for mutation callbacks. Works very well so far for me.

Why is it so complex to integrate GraphQL with Vuex? Is it because you're trying to have it reactive (for example when polling from the database?)

What if I just want to fetch the data on page load and then fetch it again when I change route? Do I still have to use this library for that, or I could just use Apollo Client + Vuex, get the promise and return the JSON from vuex?

Edit: of all the solutions above, do you know which one work best and has the smallest footprint?

You can now use vue-supply with vue-apollo to use apollo data in your vuex store easily. I'll make some examples soon.

@Akryum This is amazing :)

@Akryum - Awesome stuff! 👍

But, do I understand correctly, vue-supply only hooks into the subscription system of Apollo? How about normal queries and mutations? Will they also work? And will it still be possible to add queries, mutations and now subscriptions directly into components?

Scott

vue-supply is useful for both queries and subscriptions. Mutations can be done outside without​ problem.

@Akryum I think it would be beneficial to have a demo/boilerplate that shows GraphQL integration with Vue+Vuex, because it seems quite complex to set up, as there are many libraries involved: apollo client, vue-apollo, vue-supply, vuex

That's what I meant with "examples soon". :wink:

@Akryum Been reading through some of your examples and docs in project Readme, but would really like to see an example project or tests using it. Cheers!

@kristianmandrup i think there is something used with apollo and vuex https://github.com/vuejs/vue-curated-client

thanks @Samuell1 I think this might be an example to have to vue + apollo + vuex. I really would like to take benefit of all three.

I'm just starting to figure this out. I have experience with Vue/Vuex but barely starting to use Apollo.

First a couple of points:

  1. Doing API calls directly in the components is an anti pattern. Doesn't matter if we are considering REST or GraphQL.
  2. When using Vuex, all queries to an API have to be done in the store actions since these can be async. Store mutations have to be sync.

Any solution that involves integrating Apollo and Vue properly means that it has to allow for integration with Vuex, and most importantly Vuex modules.

Considering all this, it seems to me the solution is simply to move GraphQL queries and GraphQL mutations to store actions, and then create actions for subscribing and unsubscribing to GraphQL resources.

Can anyone explain why this approach is not valid?

@PierBover That's exactly how I'm using Vue, Vuex and Apollo atm and it feels right.

@RichAyotte are you using vue-apollo or simply vanilla Apollo client?

vue-apollo

There is no need of using vue-apollo if you want to use Vuex.

Using the vanilla apollo-client is just simpler for integrating with Vuex by following the exact same patterns you'd use with Axios, fetch, or any other HTTP library.

You're right! All those queries and mutations belong in the store, not the components unless the app was simple and didn't use a store.

Glad you brought it up. When I found vue-apollo, I assumed it was the right tool and it was I until Vuex was introduced.

Looks like my components are going on a diet.

I've created this simple example project. Hope it is useful for someone.

https://github.com/PierBover/vuex-apollo-example-project

I hope I am not misunderstanding the direction of this conversation, but, isn't the whole idea of GraphQL and componentizing the queries (i.e. having the queries with the components) why it was created in the first place? I mean, that was one of its main selling points, when Facebook first open sourced it.

Let me ask this as a question. How can components be portable and properly reusable, when their data fetching (or mutating) code is placed in some depths of a different area of the application?

Here is the video from 2015 describing what I mean:

https://www.youtube.com/watch?v=9sc8Pyc51uU

The part with Jing explains the developer experience I'm talking about. That's not to say, for complex components, everything should be on one file, but certainly, the query code should be with the component someway, somehow. 😄

Scott

The relevant part of the video start at https://youtu.be/9sc8Pyc51uU?t=12m45s or the lifecycle proposed at https://youtu.be/9sc8Pyc51uU?t=14m31s

From what I understand, Jing proposes:
View -> Server -> Store -> View

And what I think @PierBover is proposing and I think is better:
View <-> Store <-> Server

I just would like to chip in here: the greatest joy might be, if vue-apollo uses vuex internally and the $apollo component is pushing the states internal into namespaced vuex states.. So the whole vue-apollo might get syntactic sugar and behind the scenes @PierBover approach would happen. Not sure if I'm on the wood way though

And what I think @PierBover is proposing and I think is better:
View <-> Store <-> Server

Yes. That's how Vuex was designed. Precisely to decouple the front end data layer from the front end presentation layer.

Source

Edit:

From what I understand, Jing proposes:
View -> Server -> Store -> View

This is coupling your data layer with your presentation layer. If you do this with a typical REST API, then one day you might want to switch to GraphQL and you will be forced to modify all your components. Next year you will want to switch to GraphQL 2 or whatever comes, same problem.

Another problem of this approach is that any logic related to communicating to the API will be spread on all your components. That code won't be reusable and it will become an unmaintainable jungle. Instead of having all your data layer code in one nice place, you will have dozens of duplicated pieces across your project.

Edit 2:

The relevant part of the video start at https://youtu.be/9sc8Pyc51uU?t=12m45s or the lifecycle proposed at https://youtu.be/9sc8Pyc51uU?t=14m31s

Redux (from where Vuex took the inspiration) is precisely designed to solve this problem, and was released a couple of months after this talk.

@PierBover in your github example if the database is updated am I right in assuming that it won't update the component so you need some kind of polling mechanism?

Correct @microcipcip but there's no need for polling, you can use subscriptions.

If I have time later I will implement those in my example but the idea is that you'd need an action to activate a subscription and another to deactivate it.

You could want for example to keep the subscription open no matter which components are using the data. For example imagine you have a chat in your application you might want to keep on receiving chat messages even when you are not showing the chat component to the user.

Of course your server has to support subscriptions. Graphcool has subscriptions enabled by default.

Here is the document from Apollo Server on how to implement those on Node:
http://dev.apollodata.com/tools/graphql-subscriptions/index.html

@PierBover I see, let me know if you add that example then, I couldn't remember they were called subscriptions ;p

This is coupling your data layer with your presentation layer.

It's coupling data requests and mutations with the UI, yes, as queries. And I'd only do it with GraphQL. I'd never do this with REST, because REST isn't made for componentized requests or rather, getting it there would a royal PITA, if it's even possible.

Next year you will want to switch to GraphQL 2 or whatever comes, same problem.

I highly doubt the spec will change that greatly.

That code won't be reusable and it will become an unmaintainable jungle. Instead of having all your data layer code in one nice place, you will have dozens of duplicated pieces across your project.

I disagree. The queries are completely reusable along with the component. Only the queries that are called for in a "page" (i.e. a root component for the page and its children) are requested. There isn't really any duplication, unless you have tons of similar components, which I'd then question your programming.

Think about this. If you want to change a component and add or remove properties/ data, then you'd just update the component and its GraphQL queries. Everything that needs to be changed is there front and center in one place. That is a win in my book.

What is the use of having a query language that can be componentized, if you don't use it that way?

Scott

I highly doubt the spec will change that greatly.

That's what people said 2 years ago when they were building stuff with REST.

Think about this. If you want to change a component and add or remove properties/ data, then you'd just update the component and its GraphQL queries. Everything that needs to be changed is there front and center in one place. That is a win in my book.

That's a naive approach since you are only considering reading data. When writing it gets more interesting since you need to take into account validation.

Are you going to do validation in your components too?

What is the use of having a query language that can be componentized, if you don't use it that way?

Well... a) What makes you think moving your queries outside of the presentation components is not _componentizing_ your queries? and b) GraphQL is (as it's name implies) purely a query language, just like SQL. It's agnostic on how you use it.

That's a naive approach since you are only considering reading data.

Um, no I'm not. Form components need mutation queries for sure.

Are you going to do validation in your components too?

Absolutely -> https://github.com/monterail/vuelidate

What makes you think moving your queries outside of the presentation components is not componentizing your queries?

As long as the queries used for the component are in the same file or in a file with the component, i.e. so I can call everything as a single UI component, that is componentized in my mind. Anything else isn't.

Scott

Absolutely -> https://github.com/monterail/vuelidate

You are talking about UI input validation, not _data_ validation. And yeah, you could move the problem to the API but if you are doing anything moderately serious you are going to need to validate on the client too.

Anyway, it's cool. Use whatever works for you.

I already faced the challenge of doing queries with Firebase on a couple of medium sized React projects. My initial approach was exactly what you are defending, and it was a complete failure in the long run.

A Vue component's concerns should be:

  • Visual representation of data
  • Input interface

It should not be concerned with:

  • Where the data came from such as a remote server, cached, local event, hardware sensor, single or multiple sources etc.
  • How it was fetched such as REST, GraphQL, Socket, RPC etc

If you can guarantee that data that your component is using will always be accessed via GraphQL and it only comes from a single source then you're fine. If you can't, a store abstraction will keep your components lighter, more modular, more easily testable and it will greatly reduce the refactoring effort when the data source changes.

Hey @microcipcip I've updated my example with subscriptions.

https://github.com/PierBover/vuex-apollo-example-project

You are talking about UI input validation, not data validation.

Please explain what you mean, but in terms of using GraphQL. There must be something I'm not understanding, which I'd most definitely like to.

I already faced the challenge of doing queries with Firebase on a couple of medium sized React projects.

As I said. I wouldn't do queries in components with REST either. There isn't a client side library built for it, as GraphQL is (i.e. with componentization in mind).

If you can guarantee that data that your component is using will always be accessed via GraphQL and it only comes from a single source then you're fine.

If there are multiple sources needed for an application, they are irrelevant for the client with GraphQL, because the backend would have the task of ingesting any external APIs. So, if the app is using GraphQL, it will always have only one single endpoint. That's one of the main selling points as to why Meteor wanted GraphQL in their tool belt.

GraphQL is a query language especially built for UI developers. It's made to be aligned with components. It's a paradigm change.

Edit- Btw, I'm working with a project called Quasar, which uses Vue and I've started a very basic GraphQL wrapper. This is going to be enhanced considerably over the next couple of months. Keep an eye out for it. 😄

Scott

It took me a month of using Apollo Client with Vuex to realize that I was wrong about putting GraphQL queries in a Vuex store. I understand now why queries belong in components. There's nothing like experience to help one understand.

Apollo Client has its own store which it manages so copying that data into Vuex store actually removes many of the benefits that Apollo offers such as optimized fetching, caching, and updating its local data store automatically but most importantly query validation.

How confident would you be about dropping the property of an object in a Vuex store? How would you know which components depend on it? With GraphQL, it's simple, just use something like eslint-plugin-graphql and know immediately which components you broke.

Vuex's purpose is to simplify local state management. GraphQL's purpose is to simplify foreign state management. They're different for the moment. What I would like to see is the ability to use GraphQL for local state management as well. In other words, I'd like to query the Vuex store with GraphQL. Has anyone done this?

I'll just leave this quote by Eric Elliott here and unsubscribe from this issue since I really have nothing more to add:

Mingling data fetching, data manipulation and view render concerns is a recipe for time-traveling spaghetti.

Source

@PierBover thanks for your input, it has been thought provoking for me at least.

Just to be clear. I now believe that the query belongs in the component, not in the store because queries are rarely shared between components. It is the data that's shared, not the queries. Queries are usually written specifically for each component.

The View <-> Store <-> Server pattern is still correct and is still the one that I'm using. None of that has changed.

Also my arguments about where the data comes from and how it's fetched can still be reconciled with this pattern because instead of standardising on a particular store such as Vuex, I'm standardising the language to query the data. The resolver functions now become responsible for the lower level data locating and fetching work.

I'm thinking now that there might be a way to write GraphQL resolvers in Vuex which would let me query everything in GraphQL. Until then, I'm happy to query Vuex's store with plain ol` JavaScript and lodash and use GraphQL for server side data.

Here's another quote from that article.

Side side note: Relay and Falcor are other interesting solutions for state management, but unlike Redux and MobX, they must be backed by GraphQL and Falcor Server, respectively, and all Relay state corresponds to some server-persisted data. AFAIK, neither offers a good story for client-side-only, transient state management. You may be able to enjoy the benefits of both by mixing and matching Relay or Falcor with Redux or MobX, differentiating between client-only state and server-persisted state. Bottom line: There is no clear single winner for state management on the client today. Use the right tool for the job at hand.

I'm thinking now that there might be a way to write GraphQL resolvers in Vuex

There are no resolvers in the GraphQL client, are there?

Scott

I don't understand your comment @PierBover. The usage of Redux and/ or Vuex (i.e. the Flux architecture) with Vue is to undo the time-traveling spaghetti.

Scott

so , I can not use the vuex in vue-apollo now?

@yhhwpp vue-apollo is only binding, they need to integrate other stores in apollo-client, because still if you save data from apollo-client in vuex they are still in redux store

I don't understand your comment @PierBover. The usage of Redux and/ or Vuex (i.e. the Flux architecture) with Vue is to undo the time-traveling spaghetti.

Precisely, since @RichAyotte is arguing in favor of not using Vuex (or any other centralized state manager) and making GraphQL queries directly in the components.

@PierBover That's not quite what I said. Apollo Client abstracts the centralized state manager and it is this abstraction that I'm in favour of. It automatically manages the central state from the provided GraphQL queries. Also Apollo Client only works with remote data so I'm very much in favour of using Vuex for local shared state.

I would like to eventually write something like:

const remoteApolloClient = new ApolloClient({
    networkInterface: createNetworkInterface({ uri: '/api/graphql'})
})

const localApolloClient = new ApolloClient({
    store: new Vuex.Store({...})
})

const apolloProvider = new VueApollo({
    clients: {
        remoteApolloClient
        , localApolloClient
    }
})

Apollo Client abstracts the centralized state manager and it is this abstraction that I'm in favour of.

Well no because Apollo Client is not a state manager. Its concern is actually related to the network layer of your application. Sure it has a cache but you cannot use it to centralise application state or inject custom logic to manipulate data.

What if you have a product basket and want to update the little number in the basket icon from the top navigation bar when a user adds a product from the products list component?

What if you want to do some data manipulation on the list of products received from the server? Say you want to extract the available colors for some other component that allows you to filter your product list based on those colors.

Apollo Client has no way of solving those situations (nor it shouldn't).

I would agree that a component should be responsible for managing its own internal state, but once you enter into application state realm you need a state manager which Apollo Client isn't.

I would agree that a component should be responsible for managing its own internal state, but once you enter into application state realm you need a state manager which Apollo Client isn't.

Now I understand where I was missing your point. Now I've got it. 😄

Have you used Vue-Supply yet?

Scott

Have you used Vue-Supply yet?

No I haven't but it looks very _noice_ and it makes sense:

Apollo Client / Firebase / Meteor / Ably / Whatever <-> Vue-Supply <-> Vuex <-> Vue components

I see the value for subscriptions, but why not write regular queries in store actions though?

Thanks for the suggestion btw, I just noticed I missed it in previous comments...

😝

I see the value for subscriptions, but why not write regular queries in store actions though?

For example, everything in Apollo is Observable-based, which means you could refetch some data somewhere in your app, and all the component that somewhat consumes the same data get updated. So this "subscription" model is still usefull in some cases, even if there are no real-time/subscription data behind.

Hi,

I'm about to integrate apollo in a vue / vuex app. Even if I'm relatively new to vue, I come from react / redux so the migration isn't that hard. But I've just begun a few days ago to learn GraphQL & Apollo for my new job. So you may notice I struggle a bit to fully grasp the apollo / graphQL paradigm.

My first instinct, as @RichAyotte and @PierBover argued, would be to use the vanilla apollo client with vuex. But after reading the whole thread, and now that I understand that apollo uses redux under the hood, duplicating the redux state into vuex feelds very dirty...

@RichAyotte, you seem to have changed your mind about all GrapqhQL queries going through vuex actions (which is how I would do it). So what would be your approach now? Would you use vue-apollo directly in "smart", higer level components and pass the data down the component tree? Without using Vuex at all for graphql data? Or would you use vue-supply?

@Akryum, you talked about making an example, I've found your demo at vue conf with vue + vuex + vue-supply + vue-apollo. From vue-supply's doc, I understand that it's used to "proxy" apollo's redux state with vuex getters, instead of duplicating data. This way we can still use the vuex store as the global state management tool for our app, which is nice. Am I getting it right ?

I'll try to understand how vue-supply works by starting to implment it into my project and see what it really does.

Hi @SebT,

Each component contains GraphQL queries that describe the remote data needed for that component as vue-apollo was designed and contains Vuex references for local shared data. No data is ever passed directly between component otherwise you'd be binding the components, undoing modularity and creating a complicated mess.

The querying language for Vuex is JavaScript which works fine with a bit of help from lodash but less declarative than GQL. Standardising on GQL for all queries, local and remote, would be very nice.

Ok @RichAyotte so now you use vue-apollo directly from components. And still use vuex for app local state, as usual.

So the data requested with vue-apollo is bound to the component and never ends up in the vuex store because vue-apollo is designed to get data for a specific view, not the whole app, right?

@SebT Yes, exactly.

(Note) Apollo Client 2.x will allow replacing redux with vuex. See this

That's good news. Thanks Guillaume!

Scott

Great stuff. Apollo is reduced to the role of humble query fetcher at the moment in my Vuex app, which offends the sensibilities of both myself and Apollo itself.

After reading through all of this I'm confused what the preferred method is. Do I have this right?

  • Handle all data queries (fetch, mutations, etc...) directly using vue-apollo
  • Handle all the rest through vuex

@RedShift1 yes. At least that's what I do.

And when I need to interact with data from apollo in vuex I just store their ids in the store. Then, in my components, I use these ids to do apollo queries & mutations, that can make use of the apollo cache.

Related: https://www.apollographql.com/docs/link/links/state.html

Its not vuex, but you can manage all your application's state in graphql's cache using the new apollo-link-state.

interesting discussion... I'm fluid with vuex and learning the graphql paradigm right now.
I can see the points made about attaching queries to components vs vuex actions and I think both approaches have their advantages, however...
Looking at this example of a mutation with apollo client directly inside a component makes me wonder. Do we really have to write this "mess" in order to link component data to graphql??

createPost.vue
Just looking at one field "description", you need to do stuff in line 43,44,47,53,61,64. That's ridiculous. Imagine having 5 or 10 fields. I suppose this could be reduced with graphql 'fragments', but still, this doesn't feel right. If this is really the only way to link vue components to graphql, I'd rather stick to vuex and maybe use graphql inside vuex actions.

Are there any best practices evolving round vue and graphql/vuex? I'm still new to graphql so still exploring. Thanks.

We are a few months on and this is still no clearer
I still don't feel confident with using vue-apollo on a large scale application
@PierBover would you still recommend the Vuex/ Apollo approach ?

@beebase What did you end up doing?

@alajfit I sticked to vuex and I'm using it as a local offline database that syncs with a socketio / arangodb server. I LOVE the reactivity and the way you can store data in vuex.

Regarding graphql. I still feel there's a lot of code overhead needed on the client side to keep offline data in sync with the server. Also building the resolvers serverside is a LOT of work, although prisma and hasura.io look promising. Eventually I think I'll switch to graphql once products like apollo, prisma and hasura have evolved more and are settled.

@beebase I agree prisma has too many changes in a short amount of time. It hasn't matured enough to fully rely on.

@beebase You dont need to write that mess, its more like that example is writed more simplier way to understand. I recommed to look at more actual/complex examples like this: https://github.com/Akryum/vue-summit-app

Any news on this? Does plugin for integrating vuex + apollo exist, or something?

After reading this whole thread I feel like I learned a lot and still have no idea of what's the proper way to do this. :)

@diguliu - I believe the state management of choice should now be apollo-link-state.

Scott

384 Seems apollo-link-state becomes a replacment for vuex. Any drawbacks to this approach?

I'm now delving into this solution. I'll report back, once I know how good (or bad) it works.

Scott

I'm not sure if this was there before but Apollo Link can be used as stand alone without cache. See the docs here. This way you can build a typical Vue + Vuex application and completely avoid ApolloClient and InMemoryCache. Of course in this case you won't be using Vue Apollo either.

I tested it and it seems to work perfectly.

Then you miss the benefits of Apollo client and have to manage all the
state yourself. 😸

Le ven. 9 nov. 2018 à 09:41, Özgür Uysal notifications@github.com a
écrit :

I'm not sure if this was there before but Apollo Link can be used as stand
alone without cache. See the docs here
https://www.apollographql.com/docs/link/#standalone. This way you can
build a typical Vue + Vuex application and completely avoid ApolloClient
and InMemoryCache. Of couse in this case you won't be using Vue Apollo
either.

I tested it and it seems to work perfectly.


You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/Akryum/vue-apollo/issues/7#issuecomment-437437525,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACqyfMYNn5NnUM-oc8-Di_i49gVYRVT6ks5utb5YgaJpZM4KN3-U
.

Then you miss the benefits of Apollo client and have to manage all the state yourself. 😸

Isn't that the point of Vuex?

Yeah but with Apollo client you don't even need Vuex.

Le ven. 9 nov. 2018 à 09:46, Pier Bover notifications@github.com a écrit :

Then you miss the benefits of Apollo client and have to manage all the
state yourself. 😸

Isn't that the point of Vuex?


You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/Akryum/vue-apollo/issues/7#issuecomment-437438855,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACqyfHCxam4MsYMCuZOaFmHBhbKR3iD2ks5utb91gaJpZM4KN3-U
.

I see... it seems Apollo client is a whole new beast form the last time I checked it.

🤯

@Akryum Yes, absolutely. But I'm really not a fan of the way you interact with Apollo cache or apollo-link-state. On the other hand Vuex is so great.

EDIT: Having said that, for many projects, using ApolloClient and apollo-link-state as local state management would probably be the best fit as they do eliminate a lot of code for local state management.

@Akryum - Will Vue-Apollo work with the new Apollo Client 2.5.0 Alpha? Which btw to everyone, now includes link-state.

I'd test myself, but have other things to attend to. Just a reassuring "yes" or a disappointing "no" would suffice. 😄 If it is "no", how long do you think it would take until you can get Vue-Apollo updated?

Scott

As this is such a hot discussion it might be a good idea to reflect some of these thoughts on how Vuex does and doesn't integrate with Apollo on the "Local state" part of the documentation?
https://akryum.github.io/vue-apollo/guide/local-state.html

@ecker00 i dont get that example. Where is updateHello defined?

@rnenjoy Neither do I, but I don't have any experience with Apollo and GraphQL yet, so I would not be the right person to update the documentation.

@ecker00 @rnenjoy you can see example here https://codesandbox.io/s/zqqj82396p and resolvers const is where is it defined

Yeah but with Apollo client you don't even need Vuex. Le ven. 9 nov. 2018 à 09:46, Pier Bover notifications@github.com a écrit :

Then you miss the benefits of Apollo client and have to manage all the state yourself. smile_cat Isn't that the point of Vuex? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#7 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/ACqyfHCxam4MsYMCuZOaFmHBhbKR3iD2ks5utb91gaJpZM4KN3-U .

Hi,

You may not need Vuex but what about an application previously written using vuex ? I feel like it is all or nothing. How do you make an existing application using vuex compatible with apollo. We don't really want to switch completely from one to the other otherwise it will break quite a few components. So, we may not need vuex but how could we use it anyway smoothly with apollo such that we don't loose all the benefits from apollo. Do you any code samples we could use as an example ?

Regards,

@eclar you can keep using Vuex as your central store, but instead of making calls to your REST API with Axios, you just make calls and/or subscriptions with Apollo client and feed Vuex with the responses.

I made this example some time ago with an old version of Apollo client.

https://github.com/PierBover/vuex-apollo-example-project/blob/master/store.js

Don't take this at face value, it's just a quick and dirty example. For a more realistic approach I would put all the GraphQL code outside of Vuex.

Thank you for the answer. Do we have a good practice for this sort of behaviour ? Maybe @Akryum could clarify ?

If one were to remove the Vuex dependency and rely solely on vue-apollo's store. Updating the cache on successful data response. What is the recommended way to replace the functionality Vuex getters affords? To apply some logic when accessing previously stored data

Say I've a graph query that returns a list of numbers and I'd like to reduce down to a total based on a user input range, or get the value for a matching ID etc.

Many thanks!

@oller - Can you expand on your use case? What kind of component would offer a list of data, to be then reduced to a range or single id? If you need a range, wouldn't that be an input variable to the query to begin with?

Scott

Hey @smolinari , thanks for the response.

Sorry that wasn't the best worded example. Lets say I've a query that returns leads...

query leads {
  leads {
    uuid
    title
    client {
      uuid
      fullname
    }
    lastInteraction
    notes
    state
    target
...
}

The state value is a string ID, which can be one of 7 values. Depending on the value of the state I want to apply a contextual css class to set a color for the leads of that state in a list. In Vuex, I previously had this leadStates array (an array of objects with id, label, desc) hardcoded in the applicable module and I could getClassForLeadState amongst some other utility functions getIndexForLeadState, getLabelForLeadState and getTargetTotalForLeadState (which given a lead state will reduce down a total of the target properties. Having already loaded leads it seems quite bloated to send all these additional queries back to the server for simple reductions etc.

 getClassForLeadState: state => leadState => {
    let className
    switch (state.leadStates.findIndex(i => i.id === leadState)) {
      case 0:
      case 1:
        className = 'success'
        break
      case 2:
        className = 'warning'
        break
      case 3:
       ...
 },
  getLabelForLeadState: state => leadState => {
    return state.leadStates.find(ls => ls.id === leadState).label
  },
  getTargetTotalForLeadState: state => leadState => {
    return state.leads
      .filter(lead => lead.state === leadState)
      .reduce((prev, current) => prev + current.target, 0)
  }

How would a scenario like this best be implemented if I was looking to remove Vuex? Multiple components need to be able to access these getters, so I can't just factor this into a single component. Should I factor out a common helper file? look to load this logic into the Apollo store perhaps with @client ? look to move these into separate queries?

I could add some of these into new graphQL queries, but I don't want to start adding css presentational classes into the graphQL, that's no use. And it seems crazy to ask for a subset of data again when i've already the whole list and i just want to match against a key/value, but perhaps that's just the graphQL paradigm and i've not totally got my head around it yet.

Any input gratefully received!

Thanks!

@oller - not sure this will answer your question completely. But, I think it might get you thinking in a certain direction. Here is a Todo app with client-link-state and also the situation of setting styling, etc. All those "gets" you had before simply aren't needed, once the data is set. Where the data comes from doesn't matter. It just needs to be available and "set", when the component is rendered, either by calling it from a server a file or set in the data prop. After that, it is automatically reactive and that is the beauty of Vue. 👍

https://codesandbox.io/s/k3621oko23

I hope it helps. 😃

Scott

February 2019 and we still haven't figured this out.
Sigh.

(BTW, why is this issue closed?)

@AnalyzePlatypus - Why not figured out? Apollo Client has its own state management built in and therefore Vuex is not needed at all.

My example To-do app above demonstrates this ability.

Scott

Was this page helpful?
0 / 5 - 0 ratings