title: Vue, Apollo, GraphQL: "TypeError: Cannot read property 'watchQuery' of undefined"
body: <p>I am learning <code>Vue</code> and struggling with Browser Error that I have no idea how to approach. The app is using Apollo and GraphQL. The error is being thrown during navigation to <code>Posts.vue</code> component when the <code>infiniteScrollPosts</code> Query should be resolved.</p>
  <pre><code>vue.runtime.esm.js:619 [Vue warn]: Error in created hook: &quot;TypeError: Cannot read property 'watchQuery' of undefined&quot;
  
  vue.runtime.esm.js:1888 TypeError: Cannot read property 'watchQuery' of undefined
      at DollarApollo.watchQuery (vue-apollo.esm.js:1256)
      at SmartQuery.executeApollo (vue-apollo.esm.js:798)
      at SmartQuery.start (vue-apollo.esm.js:561)
      at SmartQuery.autostart (vue-apollo.esm.js:470)
      at DollarApollo.addSmartQuery (vue-apollo.esm.js:1334)
      at VueComponent.launch (vue-apollo.esm.js:1943)
      at invokeWithErrorHandling (vue.runtime.esm.js:1854)
      at callHook (vue.runtime.esm.js:4219)
      at VueComponent.Vue._init (vue.runtime.esm.js:5008)
      at new VueComponent (vue.runtime.esm.js:5154)
  </code></pre>
  <p><a href="https://i.stack.imgur.com/NeOoh.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/NeOoh.png" alt="enter image description here" /></a></p>
  <p>This is my component:</p>
  <pre><code>&lt;template&gt;
      &lt;v-container v-if=&quot;infiniteScrollPosts&quot;&gt;
          &lt;div v-for=&quot;post in infiniteScrollPosts.posts&quot; :key=&quot;post._id&quot;&gt;
              &lt;img :src=&quot;post.imageUrl&quot; height=&quot;100&quot; /&gt;
              &lt;h3&gt;{{ post.title }}&lt;/h3&gt;
          &lt;/div&gt;
          &lt;v-btn @click=&quot;showMorePosts&quot; v-if=&quot;showMoreEnabled&quot;&gt;Fetch More&lt;/v-btn&gt;
      &lt;/v-container&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
      import { INFINITE_SCROLL_POSTS } from &quot;../../queries&quot;;
  
      const pageSize = 2;
  
      export default {
          name: &quot;Posts&quot;,
          data() {
              return {
                  pageNum: 1,
                  showMoreEnabled: true
              };
          },
          apollo: {
              infiniteScrollPosts: {
                  query: INFINITE_SCROLL_POSTS,
                  variables: {
                      pageNum: 1,
                      pageSize
                  }
              }
          },
          methods: {
              showMorePosts() {
                  this.pageNum++;
                  this.$apollo.queries.infiniteScrollPosts.fetchMore({
                      variables: {
                          pageNum: this.pageNum,
                          pageSize
                      },
                      updateQuery: (prevResult, { fetchMoreResult }) =&gt; {
                          console.log(&quot;previous result&quot;, prevResult.infiniteScrollPosts.posts);
                          console.log(&quot;fetch more result&quot;, fetchMoreResult);
  
                          const newPosts = fetchMoreResult.infiniteScrollPosts.posts;
                          const hasMore = fetchMoreResult.infiniteScrollPosts.hasMore;
                          this.showMoreEnabled = hasMore;
  
                          return {
                              infiniteScrollPosts: {
                                  __typename: prevResult.infiniteScrollPosts.__typename,
                                  posts: [...prevResult.infiniteScrollPosts.posts, ...newPosts],
                                  hasMore
                              }
                          };
                      }
                  });
              }
          }
      };
  &lt;/script&gt;
  </code></pre>
  <p>The <code>GraphQL</code> Query:</p>
  <pre><code>export const INFINITE_SCROLL_POSTS = gql`
      query(
          $pageNum: Int!,
          $pageSize: Int!    
      ) {
          infiniteScrollPosts(
              pageNum: $pageNum, 
              pageSize: $pageSize
          ) {
              hasMore
              posts {
                  _id
                  title
                  imageUrl
                  categories
                  description
                  likes
                  createdDate
                  messages {
                      _id
                  }
                  createdBy {
                      _id
                      userName
                      avatar
                  }
              }
          }
      }
  `;
  </code></pre>
  <p>Resolver:</p>
  <pre><code>module.exports = {
      Query: {
          (...)
          infiniteScrollPosts: async (_, { pageNum, pageSize }, { postSchema }) =&gt; {
              let posts;
              if (pageNum === 1) {
                  posts = await postSchema.find({}).sort({ createdDate: &quot;desc&quot; }).populate({
                      path: &quot;createdBy&quot;,
                      model: &quot;User&quot;
                  }).limit(pageSize);
              } else {
                  const skips = pageSize * (pageNum - 1);
                  posts = await postSchema.find({}).sort({ createdDate: &quot;desc&quot; }).populate({
                      path: &quot;createdBy&quot;,
                      model: &quot;User&quot;
                  }).skip(skips).limit(pageSize);
              }
  
              const totalPosts = await postSchema.countDocuments();
              const hasMore = totalPosts &gt; pageSize * pageNum;
  
              return { hasMore, posts };
          },
          (...)
      },
      Mutation: {
          (...)
      }
  };
  </code></pre>
  <p>TypeDefs:</p>
  <pre><code>(...)
  
  type PostsPage {
      posts: [Post]
      hasMore: Boolean
  }
  
  type Query {
      (...)
      infiniteScrollPosts(pageNum: Int!, pageSize: Int!): PostsPage
  }
  
  (...)
  </code></pre>
  <p><code>main.js</code></p>
  <pre><code>import Vue from &quot;vue&quot;;
  import App from &quot;./App.vue&quot;;
  import router from &quot;./router&quot;;
  import Router from &quot;vue-router&quot;;
  import store from &quot;./store&quot;;
  import vuetify from &quot;./plugins/vuetify&quot;;
  import ApolloClient from &quot;apollo-boost&quot;;
  import VueApollo from &quot;vue-apollo&quot;;
  import FormAlert from &quot;./components/Shared/FormAlert&quot;;
  import &quot;@babel/polyfill&quot;;
  
  Vue.component(&quot;form-alert&quot;, FormAlert);
  Vue.use(VueApollo);
  
  export const apolloClient = new ApolloClient({
      uri: &quot;http://localhost:4000/graphql&quot;,
      fetchOptions: {
          credentials: &quot;include&quot;
      },
      request: operation =&gt; {
          if (!localStorage.token) {
              localStorage.setItem(&quot;token&quot;, &quot;&quot;);
          }
          operation.setContext({
              headers: {
                  authorization: localStorage.getItem(&quot;token&quot;)
              }
          });
      },
      onError: ({ graphQLErrors, networkError }) =&gt; {
          if (networkError) {
              console.log(&quot;[networkError]&quot;, networkError);
          }
  
          if (graphQLErrors) {
              for (let err of graphQLErrors) {
                  console.dir(err);
                  if (err.name === &quot;AuthenticationError&quot;) {
                      store.commit(&quot;setAuthError&quot;, err);
                      store.dispatch(&quot;signoutUser&quot;);
                  }
              }
          }
      }
  });
  
  const apolloProvider = new VueApollo({ apolloClient });
  
  Vue.config.productionTip = false;
  
  new Vue({
      apolloProvider,
      router,
      store,
      vuetify,
      render: h =&gt; h(App),
      created() {
          this.$store.dispatch(&quot;getCurrentUser&quot;);
      }
  }).$mount(&quot;#app&quot;);
  
  const originalPush = Router.prototype.push;
  Router.prototype.push = function(location) {
      return originalPush.call(this, location).catch(ex =&gt; {
          if (ex.name !== &quot;NavigationDuplicated&quot;) {
              throw ex;
          }
      });
  };
  
  Vue.use(router);
  </code></pre>
  <p>The Query itself works:
  <a href="https://i.stack.imgur.com/QP4oT.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/QP4oT.png" alt="enter image description here" /></a></p>
  <p>Would appreciate some help as you can consider me a Vue Noob :).</p>
  <p>// EDIT</p>
  <p>To answer the comments:</p>
  <p>@xadm</p>
  <p>The full list of requests present on <code>Posts</code> component load contains only one significant call to to the <code>GraphQL</code> and that's my authorization - which works and is of no consequence here. The one that returns <code>204</code>, on the other hand, is present during every other component load and I am not familiar enough with <code>Vue</code> to comment on it but since it is on all other pages then it can be discarded as irrelevant to the issue.</p>
  <p><a href="https://i.stack.imgur.com/2NajR.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/2NajR.png" alt="enter image description here" /></a></p>
  <p>As for your react smiley face, I very much prefer to work with <code>Blazor</code>.</p>
  <p>@Adam Orlov</p>
  <p>You can find an example of such usage here: <a href="https://apollo.vuejs.org/guide/apollo/pagination.html" rel="nofollow noreferrer">https://apollo.vuejs.org/guide/apollo/pagination.html</a>, you access <code>$store</code> in the same manner. Regardless, it is not relevant to the problem, you can just comment that section out as it is not even called before the button is explicitly clicked which it isn't, the problem seems to be here:</p>
  <pre><code>apollo: {
      infiniteScrollPosts: {
          query: INFINITE_SCROLL_POSTS,
          variables: {
              pageNum: 1,
              pageSize
          }
      }
  },
  </code></pre>
  <p>// EDIT 2</p>
  <blockquote>
  <p>This is not Vue related error. It's an Apollo client error. There is many similar errors in the internet about watchQuery and undefined with a different frameworks. It could be that you are using apollo to fast when page load, and it's not initialized yet. – user1093555</p>
  </blockquote>
  <p>Yeah I figured, I browsed these before posting here and found nothing that helped me to identify the problem. I thought that it might be too early in the lifecycle too but this precise code repeats in numerous tutorials and documentation pages, including the official one I linked before.</p>
  <blockquote>
  <p>I don't care about all requests ... check only POST /graphql request AND response DETAILY (headers, body) - find the DIFFERENCE between working (?) first and following (fetchMore, next page) or working request in playground ... I assume you don't know react at all, you don't see a difference/distance to all these 'templatish', quasi [not real]-component solutions – xadm</p>
  </blockquote>
  <p><code>fetchMore</code> is irrelevant because the app is not even getting there yet, it requires user to click a button first. You can comment out the entire <code>methods</code> section from my code, its not even getting to that point. I posted the screenshot of requests only for reference sake, I told you I checked the headers and bodies, the only request is the one that authorizes the user and it works (sends correct data, receives correct response). The one that should load the posts is not even called. And no, I never worked with React because I have never needed too, I learn <code>Vue</code> to expand my knowledge though I don't really need it either.</p>
  <p>I believe the point of the tutorial I am following is to show that it is possible to use apollo directly with <code>apollo: (...)</code>.</p>
  <p>I am a Vue noob but I am not an idiot, it is easy to make it work with the following modifications:</p>
  <p><code>Posts.vue</code></p>
  <pre><code>&lt;template&gt;
      &lt;v-container text-center v-if=&quot;posts &amp;&amp; posts.length &gt; 0&quot;&gt;
          &lt;div v-for=&quot;post in posts&quot; :key=&quot;post._id&quot;&gt;
              &lt;img :src=&quot;post.imageUrl&quot; height=&quot;100&quot; /&gt;
              &lt;h3&gt;{{ post.title }}&lt;/h3&gt;
          &lt;/div&gt;
          &lt;v-btn class=&quot;mt-3 info&quot; @click=&quot;showMorePosts&quot; v-if=&quot;hasMore&quot; info&gt;Fetch More&lt;/v-btn&gt;
      &lt;/v-container&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
      import { mapGetters } from &quot;vuex&quot;;
  
      const pageSize = 2;
  
      export default {
          name: &quot;Posts&quot;,
          data() {
              return {
                  pageNum: 1
              };
          },
          created() {
              this.handleGetInfiniteScrollPosts();
          },
          computed: {
              ...mapGetters([&quot;posts&quot;, &quot;hasMore&quot;])
          },
          methods: {
              handleGetInfiniteScrollPosts() {
                  this.$store.dispatch(&quot;getInfiniteScrollPosts&quot;, {
                      pageNum: this.pageNum,
                      pageSize
                  });
              },
              showMorePosts() {
                  this.pageNum++;
                  this.handleGetInfiniteScrollPosts();
              }
          }
      };
  &lt;/script&gt;
  </code></pre>
  <p><code>store.js</code>:</p>
  <pre><code>(...)
  Vue.use(Vuex);
  (...)
  
  export default new Vuex.Store({
      state: {
          posts: [],
          hasMore: true
          (...)
      },
      mutations: {
          (...)
          setPosts: (state, payload) =&gt; {
              state.posts = payload;
          },
          updatePosts: (state, payload) =&gt; {
              state.posts = [...state.posts, ...payload];
          },
          setHasMore: (state, payload) =&gt; {
              state.hasMore = payload;
          },
          (...)
      },
      actions: {
          (...)
          getInfiniteScrollPosts: ({ commit }, payload) =&gt; {
              if (payload.pageNum === 1) {
                  commit(&quot;setPosts&quot;, []);
              }
              apolloClient.query({
                  query: INFINITE_SCROLL_POSTS,
                  variables: payload
              }).then(({ data }) =&gt; {
                  commit(&quot;updatePosts&quot;, data.infiniteScrollPosts.posts);
                  commit(&quot;setHasMore&quot;, data.infiniteScrollPosts.hasMore);
              });
          },
          (...)
      },
      getters: {
          posts: state =&gt; state.posts,
          hasMore: state =&gt; state.hasMore,
          (...)
      }
  });
  </code></pre>
  <p>Proof:
  <a href="https://www.dropbox.com/s/r6ol4htbu5503hs/VueInfinitePosts.mp4?dl=0" rel="nofollow noreferrer">https://www.dropbox.com/s/r6ol4htbu5503hs/VueInfinitePosts.mp4?dl=0</a></p>
  <p>In this case both relevant graphql requests are resolved with correct response bodies and there are no errors whatsoever. This is beside the point however because as I said that's not what this post is about.</p>