apollo icon indicating copy to clipboard operation
apollo copied to clipboard

Empty SSR page content when `pollInterval` is set

Open chriscalo opened this issue 5 years ago • 6 comments

Version

v4.0.0-rc.19

Reproduction link

https://github.com/chriscalo/nuxt-apollo-polling

Steps to reproduce

  1. Create Nuxt app (yarn create nuxt-app nuxt-apollo-polling)
  2. Add @nuxtjs/apollo and create basic graphql server
  3. Add graphql query to a .vue page and include a pollInterval

(See the exact steps I've taken in the repro repo commits)

For example, in pages/index.vue:

<script>
import Logo from '~/components/Logo.vue'
import { User } from '~/queries/auth.graphql'

export default {
  apollo: {
    user: {
      query: User,
      prefetch: true,
      // adding this `pollInterval` value causes the problem
      pollInterval: 1000,
    },
  },
  components: {
    Logo
  }
}
</script>

What is expected ?

Adding a pollInterval to an Apollo query works without breaking anything.

What is actually happening?

Adding a pollInterval value causes the server to return empty page content. You can see this by right-clicking the page and selecting View Page Source. All of the Nuxt <scripts> and containing <div>s are there, but the page content is empty.

Here's what I see in the <body> when the pollInterval is present:

    <div data-server-rendered="true" id="__nuxt"><!----><div id="__layout"><div><script>window.__NUXT__={layout:"default",data:[{}],error:null,serverRendered:true,apollo:{defaultClient:Object.create(null)},logs:[]};</script><script src="/_nuxt/runtime.js" defer></script><script src="/_nuxt/pages/index.js" defer></script><script src="/_nuxt/pages/index.a474644729814055f4ea.hot-update.js" defer></script><script src="/_nuxt/commons.app.js" defer></script><script src="/_nuxt/vendors.app.js" defer></script><script src="/_nuxt/app.js" defer></script>

Compare that with what's in the <body> when the pollInterval is removed:

<div data-server-rendered="true" id="__nuxt"><!----><div id="__layout"><div><div class="container"><div><svg width="245" height="180" viewBox="0 0 452 342" xmlns="http://www.w3.org/2000/svg" class="NuxtLogo"><g fill="none" fill-rule="evenodd"><path d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z" fill="#00C58E"></path> <path d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z" fill="#108775"></path> <path d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z" fill="#2F495E" fill-rule="nonzero"></path></g></svg> <h1 class="title">
      nuxt-apollo-polling
    </h1> <h2 class="subtitle">
      repro for polling bug in @nuxtjs/apollo
    </h2> <p>
      user = [email protected]
    </p> <div class="links"><a href="https://nuxtjs.org/" target="_blank" class="button--green">
        Documentation
      </a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey">
        GitHub
      </a></div></div></div></div></div></div><script>window.__NUXT__={layout:"default",data:[{}],error:null,serverRendered:true,apollo:{defaultClient:Object.create(null,{ROOT_QUERY:{writable:true,enumerable:true,value:{user:"[email protected]"}}})},logs:[]};</script><script src="/_nuxt/runtime.js" defer></script><script src="/_nuxt/pages/index.js" defer></script><script src="/_nuxt/pages/index.8be2ffa62aee797d62e6.hot-update.js" defer></script><script src="/_nuxt/commons.app.js" defer></script><script src="/_nuxt/vendors.app.js" defer></script><script src="/_nuxt/app.js" defer></script>
This bug report is available on Nuxt community (#c275)

chriscalo avatar Dec 29 '19 01:12 chriscalo

Not ideal, but the following works:

<script>
import Logo from '~/components/Logo.vue'
import { User } from '~/queries/auth.graphql'

export default {
  apollo: {
    user: {
      query: User,
      prefetch: true,
      // The server doesn't get the config, and will continue to prefetch the data
      pollInterval: process.server ? undefined: 1000,
    },
  },
  components: {
    Logo
  }
}
</script>

paulb896 avatar Jan 04 '20 09:01 paulb896

Brilliant workaround, @paulb896. 🙏

I ended up going with a short-circuit conditional. Works beautifully. Thanks for pointing this out.

<script>
import Logo from '~/components/Logo.vue'
import { User } from '~/queries/auth.graphql'

export default {
  apollo: {
    user: {
      query: User,
      prefetch: true,
      // The server doesn't get the config, and will continue to prefetch the data
      pollInterval: process.client && 1000,
    },
  },
  components: {
    Logo
  }
}
</script>

I would still say this is a bug that should be fixed. But at least I now know how to work around it.

chriscalo avatar Jan 05 '20 01:01 chriscalo

Just ran into this on version 4.0.0-rc19

drewbaker avatar Apr 01 '20 05:04 drewbaker

I ran into a an issue just now where I had a pollInterval setup on the parent of a nuxt-child, and when the route changes to the nuxt-child, the next time the query runs, it crashes with this weird error:

// NOTE logging: graphQLErrors, networkError, operation, forward

Array []
 Error
    InvariantError invariant.esm.js:12
    invariant invariant.esm.js:24
    writeFieldToStore bundle.esm.js:664
    writeSelectionSetToStore bundle.esm.js:560
    writeSelectionSetToStore bundle.esm.js:551
    writeResultToStore bundle.esm.js:529
    write bundle.esm.js:875
    markQueryResult bundle.esm.js:1781
    markQueryResult bundle.esm.js:1201
    subscription bundle.esm.js:1642
    next Observable.js:322
    notifySubscription Observable.js:135
    onNotify Observable.js:179
    next Observable.js:235
    next bundle.esm.js:866
    next bundle.esm.js:866
    notifySubscription Observable.js:135
    onNotify Observable.js:179
    next Observable.js:235
    notifySubscription Observable.js:135
    onNotify Observable.js:179
    next Observable.js:235
    createUploadLink index.js:120
 undefined undefined

drewbaker avatar Apr 01 '20 19:04 drewbaker

Whats interesting is if I do something like this:

        "$route.params.id"(newVal, oldVal) {
            if (oldVal) {
                setTimeout(() => {
                    this.$apollo.queries.tickets.refetch()
                }, 1000)
            }
        }

This runs when you come back from the nuxt-child using ./ in a nuxt-link. I will get the same error with or without the setTimeout. Makes me think this is related to the refetch() method in Vue Apollo.

drewbaker avatar Apr 02 '20 01:04 drewbaker

My error was due to a GQL query missing an ID.

                project {
                    id # Without this, it caused the above error
                    timezone
                }

I figured it out after trying to figure out how the cache works and triggered another InvariantError that had a better description about missing project resource ID.

drewbaker avatar Apr 02 '20 20:04 drewbaker