Cannot stringify POJOs with symbolic keys on nuxt 2

kieusonlam
601
kieusonlam
commented a year ago

Version

v4.0.0-rc.2

Reproduction link

https://github.com/nuxt-community/apollo-module

Steps to reproduce

I get this error after update nuxt 2.0.0 to 2.1.0, revert back to 2.0.0 still geting the problem. It's happen on my both 2 project when return the value that query by apollo in asyncData:

  async asyncData({ error, app, params }){
    const post = await app.apolloProvider.defaultClient.query({
      query: postQuery,
      variables: { slug: params.slug }
    }).then(({ data }) => data && data.post)

    if (!post) return error({ statusCode: 404, message: 'Not found!!' })

    return { post }
  },

Nuxt 1.4.2 is working normally, anyone have the same problem? Another weird thing, on codesanbox, nuxt 2.1.0 still working.

What is expected ?

Shouldn't get error.

What is actually happening?

Getting error.

bug
0
dohomi
1.4k
dohomi
commented a year ago

@kieusonlam apollo-module is currently not prepared for Nuxt v2 yet. There are currently opened issues with vue-apollo and I'm not sure whats the right way to circumvent the current situation. You can follow #158 where I started to upgrade but I'm kind of stuck currently with SSR compatibility. Beside that I also don't have a Nuxt v2 project in place to run. Please feel free to contribute in the PR or open a PR for just for compatibility reasons for v2

0
evseevnntmp
0
evseevnntmp
commented a year ago

@kieusonlam Yes, i got same. Do you know reason for that?

0
bjunc
114
bjunc
commented a year ago

I ran into this error this morning when attempting to upgrade to Nuxt 2.1. After some investigating, I was able to get it to work by cloning the Apollo response. I typically do this anyway due to how Apollo returns data as read-only, but this one particular page returning the error was one area I wasn't cloning. Here's an example:

Throws error in Nuxt 2.1:

let client = app.apolloProvider.defaultClient
try {
  let { data } = await client.query({ query: USER_QUERY })
  Object.assign(store.state.user, data.user)
} catch (err) {
  // handle error
}

Works in Nuxt 2.1:

Object.assign(store.state.user, _.clone(data.user))

Note: lodash is used here, but there are other ways to "clone" responses worth investigating for your wants/needs.

I haven't yet diagnosed exactly what causes this, but it seems more to do with Nuxt than it does Apollo (and/or vue-apollo). Rather, it seems Nuxt is using the devalue module; which doesn't like symbolic keys (which happened to be returned from Apollo).

I don't know why/where Nuxt is using the devalue module. I also think it's probably wise for Nuxt to catch the error and throw a warning in this case, rather than letting devalue throw the error uncaught.

I would exercise caution if Nuxt has systemic issues with read-only or symbolic keys.

If I find out more about this, I'll post back here.

0
aldarund
963
aldarund
commented a year ago

@bjunc warning is coming instead of error. As for symbolic keys - they wasnt serializing before too, they was just silently ignored. Symbol are not really serializable thing..

0
bjunc
114
bjunc
commented a year ago

Thanks @aldarund. Also nice that you're printing the symbol keys that couldn't be stringified for debugging.

I looked a little bit more into this, and it turns out Apollo uses a symbol for the ID; which I believe is used for internal caching. That means every apollo response will trigger the warning that you've written unless the developer plucks only the serializable data. Since Apollo also sends responses frozen (read-only), I typically use Lodash's clone function anyway. That clone function (similar to the structured clone algorithm) skips symbols and essentially "unfreezes" the response data.

So I think my suggestion to anyone reading this is to clone the apollo response as now a best practice to not only unfreeze the apollo response data, but avoid the devalue warning (which will also avoid the current error before the error-to-warning PR is merged).

0
davision
5
davision
commented a year ago

I believe this has no connection with apollo. Using this code throws the same error:

  async asyncData({params}) {
    const fileContent = await import(`~/static/content/${params.slug}.md`)

    return {
      content: fileContent,
    }
  },

throws:

✖ error NuxtServerError: Cannot stringify POJOs with symbolic keys
  at walk (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:47:31)
  at /Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:49:72
  at Array.forEach (<anonymous>)
  at walk (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:49:40)
  at Array.forEach (<anonymous>)
  at walk (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:33:27)
  at /Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:49:72
  at Array.forEach (<anonymous>)
  at walk (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:49:40)
  at devalue (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/devalue/dist/devalue.umd.js:53:5)
  at Renderer.renderRoute (/Users/davidlicen/Documents/Projects/0xcert/landing-www/node_modules/nuxt/dist/nuxt.js:2042:50)
  at <anonymous>

Using Nuxt 2.1

0
bjunc
114
bjunc
commented a year ago

@davision per above, this error has more to do with the devalue module not like Symbol keys; which Apollo happens to use. I don't know what is returned from your example, but there's likely a Symbol in there. You can check by:

Object.getOwnPropertySymbols(fileContent)
0
dohomi
1.4k
dohomi
commented a year ago

@bjunc I tried your above code but for me the symbols are not removed through lodash.clone:

        const article = _clone(data) // this is from a query response of apollo
        console.log('article:', Object.getOwnPropertySymbols(article)) // => article: [ Symbol(id) ]

Any idea how to remove the symbols?

0
bjunc
114
bjunc
commented a year ago

@dohomi, I believe the above should work. However, _.clone() is shallow, so if your article has nested objects, they too will have symbols for the ID. Did you try _.cloneDeep()?

0
dohomi
1.4k
dohomi
commented a year ago

@bjunc I realized that somehow lodash.deepclone did not remove the symbols. I changed to clone-deep package and now it works as expected, thanks for the hint.

0
AndrewBogdanovTSS
69
AndrewBogdanovTSS
commented a year ago

@dohomi I tried it with v4.0.0-rc.2.2 but still got this error

0
dohomi
1.4k
dohomi
commented a year ago

@AndrewBogdanovTSS where do you see this error? As @bjunc correctly stated, you need to clone/cloneDeep your response inside of asyncData to remove the symbolic keys. This worked for me. I am using https://www.npmjs.com/package/clone-deep to make this work

0
AndrewBogdanovTSS
69
AndrewBogdanovTSS
commented a year ago

@dohomi yeah, after I switched to deep-clone in my code it started to work as expected. Thank you.

0
Informations
Bug ReportOpen
#c145 - Created a year ago