No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Hydration Apollo Client

    Help
    1
    2
    730
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • M
      mitchellmonaghan last edited by mitchellmonaghan

      Hi all,

      I’ve been tinkering around trying to learn ssr/apollo client better and was wondering if there is a better solution then what I have. Is there a cleaner way to hydrate the front end client without the use a vuex? I’m also using vueApollo and it says that it attempts to do prefetch automatically but I’m not sure how I would set the APOLLO_STATE.

      Dev mode… ssr + pwa
      Pkg quasar… v1.0.5
      Pkg @quasar/app… v1.0.4

      Apollo Boot File

      import { ApolloClient } from 'apollo-client'
      import { InMemoryCache } from 'apollo-cache-inmemory'
      import VueApollo from 'vue-apollo'
      import fetch from 'node-fetch'
      import { createHttpLink } from 'apollo-link-http'
      
      const createApolloClient = function (ssr = false) {
        const httpLink = createHttpLink({ uri: process.env.API_URL, fetch: fetch })
        const cache = new InMemoryCache()
      
        if (!ssr && typeof window !== 'undefined') {
          const state = window.__APOLLO_STATE__
      
          if (state) {
            cache.restore(state)
          }
        }
      
        // Create the apollo client
        const apolloClient = new ApolloClient({
          link: httpLink,
          cache,
          connectToDevTools: true,
          ...(ssr ? { ssrMode: true } : { ssrForceFetchDelay: 100 })
        })
      
        return apolloClient
      }
      
      export default ({ app, Vue, ssrContext }) => {
        const apolloClient = createApolloClient(!!ssrContext)
      
        const apolloProvider = new VueApollo({
          defaultClient: apolloClient,
          errorHandler ({ graphQLErrors, networkError }) {
            if (graphQLErrors) {
              graphQLErrors.map(({ message, locations, path }) =>
                console.log(
                  `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
                )
              )
            }
            if (networkError) {
              console.log(`[Network error]: ${networkError}`)
            }
          }
        })
      
        Vue.use(VueApollo)
        app.provide = ({ $apolloProvider: apolloProvider })
      
        if (ssrContext) {
          ssrContext.apolloState = JSON.stringify(apolloProvider.defaultClient.extract())
          ssrContext.apollo = apolloProvider
        }
      }
      
      

      The apolloClient.extract() here always returns {} but I still need to call it here because if ssrContext.apolloState is null quasar complains when it tries to inject apolloState into index.template.html

      index.template.html

      <% if (htmlWebpackPlugin.options.ctx.mode.ssr) { %>
            <script>
              window.__APOLLO_STATE__ = {{{ apolloState }}};
            </script>
          <% } %>
      

      listItems component

      <template>
        <div class="col">
          <ul>
            <li v-for="item in listItems" :key="item.id">
              <router-link :to="{ name: 'anotherPage', params: { name: item.name } }">{{ item.name }}</router-link>
            </li>
          </ul>
        </div>
      </template>
      
      <script>
      import GET_LIST_ITEMS from '@graphql/queries/listItems.gql'
      
      export default {
        async preFetch ({ store, currentRoute, previousRoute, redirect, ssrContext }) {
          if (ssrContext) {
            await ssrContext.apollo.defaultClient.query({
              query: GET_LIST_ITEMS,
              variables: {
                where: {}
              }
            })
      
            ssrContext.apolloState = JSON.stringify(ssrContext.apollo.defaultClient.extract())
          }
        },
      
        apollo: {
          listItems: {
            query: GET_LIST_ITEMS,
            variables: {
              where: {}
            }
          }
        }
      }
      </script>
      

      Do I have to use quasar prefetch on every component? Is there a cleaner/better way? Am I missing something? This currently works, client properly hydrated, no additional graphql requests, apollo cache is loaded without the use of vuex. I want to use apolloClient for local state management and so it would be better to not have vuex in the build.

      Would love your input! Thanks!

      1 Reply Last reply Reply Quote 0
      • M
        mitchellmonaghan last edited by mitchellmonaghan

        I was able to figure this out, with the help of the vue-apollo docs. You can set a rendered function on your ssrContext that quasar provides. I was confused by what the docs meant by context. With this you can remove the entire prefetch from the component.

        Updating files incase someone else gets stuck on this like I did.

        Updated apollo boot file

        import { ApolloClient } from 'apollo-client'
        import { InMemoryCache } from 'apollo-cache-inmemory'
        import VueApollo from 'vue-apollo'
        import fetch from 'node-fetch'
        import { createHttpLink } from 'apollo-link-http'
        
        const createApolloClient = function (isServerSide) {
          const httpLink = createHttpLink({ uri: process.env.API_URL, fetch: fetch })
          const cache = new InMemoryCache()
        
          if (!isServerSide) {
            const state = window.__APOLLO_STATE__
        
            if (state) {
              cache.restore(state)
            }
          }
        
          const apolloClient = new ApolloClient({
            link: httpLink,
            cache,
            connectToDevTools: true,
            ...(isServerSide ? { ssrMode: true } : { ssrForceFetchDelay: 100 })
          })
        
          const apolloProvider = new VueApollo({
            defaultClient: apolloClient,
            errorHandler ({ graphQLErrors, networkError }) {
              if (graphQLErrors) {
                graphQLErrors.map(({ message, locations, path }) =>
                  console.log(
                    `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
                  )
                )
              }
              if (networkError) {
                console.log(`[Network error]: ${networkError}`)
              }
            }
          })
        
          return apolloProvider
        }
        
        export default ({ app, Vue, ssrContext }) => {
          const apolloProvider = createApolloClient(!!ssrContext)
        
          Vue.use(VueApollo)
          app.provide = ({ $apolloProvider: apolloProvider })
        
          if (ssrContext) {
            ssrContext.rendered = () => {
              ssrContext.apolloState = JSON.stringify(apolloProvider.defaultClient.extract())
            }
          }
        }
        
        

        listItems component

        <template>
          <div class="col">
            <ul>
              <li v-for="item in listItems" :key="item.id">
                <router-link :to="{ name: 'anotherPage', params: { name: item.name } }">{{ item.name }}</router-link>
              </li>
            </ul>
          </div>
        </template>
        
        <script>
        import GET_LIST_ITEMS from '@graphql/queries/listItems.gql'
        
        export default {
          apollo: {
            listItems: {
              query: GET_LIST_ITEMS,
              variables: {
                where: {}
              }
            }
          }
        }
        </script>
        
        1 Reply Last reply Reply Quote 1
        • First post
          Last post