superjson icon indicating copy to clipboard operation
superjson copied to clipboard

Getting "TypeError: Cannot read property 'queries' of undefined"

Open dgrcode opened this issue 3 years ago • 2 comments

I've been trying to debug this, but after some time spent looking at breakpoints I can't manage to understand why the object to be serialised is coming as undefined.

The complete stack trace:

error - TypeError: Cannot read property 'queries' of undefined
    at /Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/accessDeep.js:28:24
    at Array.forEach (<anonymous>)
    at Object.getDeep (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/accessDeep.js:27:10)
    at apply (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/plainer.js:59:35)
    at /Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/util.js:53:16
    at Array.forEach (<anonymous>)
    at Object.forEach (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/util.js:51:28)
    at Object.applyReferentialEqualityAnnotations (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/plainer.js:74:16)
    at Object.deserialize (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/superjson/dist/index.js:44:28)
    at deserializeProps (/Users/daniel/projects/bootstrapped/visualbolt/node_modules/babel-plugin-superjson-next/dist/tools.js:110:32) {
  page: '/view/new'
}

I'm using NextJS and react query. The problem seems to come when superjson tries to serialize react query's dehydrated state.

Debugging this issue I've found what object is exactly causing the problem, but I can't manage to find a solution debugging the dist files. I'll show what works, and what breaks.

Working code

const queryClient = new QueryClient()
const templates = await prisma.template.findMany({
  where: { ownerId: userId },
  include: { fields: true },
})
queryClient.setQueryData(['templates'], templates)

return {
  props: { dehydratedState: dehydrate(queryClient) },
}

These are the JSON representation of relevant objects there:

  • template:
    [
      {
        "id": "cl6mlkijv00000glajl1xljzl",
        "label": "foo",
        "ownerId": "cl6cdyozp0007jbul3o4oqoox",
        "createdAt": "2022-08-09T19:50:25.003Z",
        "updatedAt": "2022-08-09T19:50:25.019Z",
        "fields": [
          {
            "id": "cl6mlkik800010glagppvr24i",
            "label": "Property",
            "type": "NUMBER",
            "templateId": "cl6mlkijv00000glajl1xljzl",
            "createdAt": "2022-08-09T19:50:25.003Z",
            "updatedAt": "2022-08-09T19:50:25.019Z"
          }
        ]
      }
    ]
    
  • returned props.dehydratedState:
    {
      "mutations": [],
      "queries": [
        {
          "state": {
            "data": [
              {
                "id": "cl6mlkijv00000glajl1xljzl",
                "label": "foo",
                "ownerId": "cl6cdyozp0007jbul3o4oqoox",
                "createdAt": "2022-08-09T19:50:25.003Z",
                "updatedAt": "2022-08-09T19:50:25.019Z",
                "fields": [
                  {
                    "id": "cl6mlkik800010glagppvr24i",
                    "label": "Property",
                    "type": "NUMBER",
                    "templateId": "cl6mlkijv00000glajl1xljzl",
                    "createdAt": "2022-08-09T19:50:25.003Z",
                    "updatedAt": "2022-08-09T19:50:25.019Z"
                  }
                ]
              }
            ],
            "dataUpdateCount": 1,
            "dataUpdatedAt": 1660642973913,
            "error": null,
            "errorUpdateCount": 0,
            "errorUpdatedAt": 0,
            "fetchFailureCount": 0,
            "fetchMeta": null,
            "isInvalidated": false,
            "status": "success",
            "fetchStatus": "idle"
          },
          "queryKey": [
            "templates"
          ],
          "queryHash": "[\"templates\"]"
        }
      ]
    }
    

Breaking code

const queryClient = new QueryClient()
const templates = await prisma.template.findMany({
  where: { ownerId: userId },
  include: { fields: true },
})
queryClient.setQueryData(['templates'], templates)

+ const t = templates[0]
+ queryClient.setQueryData(['templates', t.id], t)

return {
  props: { dehydratedState: dehydrate(queryClient) },
}

These are the JSON representation of the relevant objects here:

  • templates (same as before)
  • t:
    {
      "id": "cl6mlkijv00000glajl1xljzl",
      "label": "foo",
      "ownerId": "cl6cdyozp0007jbul3o4oqoox",
      "createdAt": "2022-08-09T19:50:25.003Z",
      "updatedAt": "2022-08-09T19:50:25.019Z",
      "fields": [
        {
          "id": "cl6mlkik800010glagppvr24i",
          "label": "Property",
          "type": "NUMBER",
          "templateId": "cl6mlkijv00000glajl1xljzl",
          "createdAt": "2022-08-09T19:50:25.003Z",
          "updatedAt": "2022-08-09T19:50:25.019Z"
        }
      ]
    }
    
  • returned props.dehydratedState:
    {
      "mutations": [],
      "queries": [
        {
          "state": {
            "data": [
              {
                "id": "cl6mlkijv00000glajl1xljzl",
                "label": "foo",
                "ownerId": "cl6cdyozp0007jbul3o4oqoox",
                "createdAt": "2022-08-09T19:50:25.003Z",
                "updatedAt": "2022-08-09T19:50:25.019Z",
                "fields": [
                  {
                    "id": "cl6mlkik800010glagppvr24i",
                    "label": "Property",
                    "type": "NUMBER",
                    "templateId": "cl6mlkijv00000glajl1xljzl",
                    "createdAt": "2022-08-09T19:50:25.003Z",
                    "updatedAt": "2022-08-09T19:50:25.019Z"
                  }
                ]
              }
            ],
            "dataUpdateCount": 1,
            "dataUpdatedAt": 1660643358366,
            "error": null,
            "errorUpdateCount": 0,
            "errorUpdatedAt": 0,
            "fetchFailureCount": 0,
            "fetchMeta": null,
            "isInvalidated": false,
            "status": "success",
            "fetchStatus": "idle"
          },
          "queryKey": [
            "templates"
          ],
          "queryHash": "[\"templates\"]"
        },
        {
          "state": {
            "data": {
              "id": "cl6mlkijv00000glajl1xljzl",
              "label": "foo",
              "ownerId": "cl6cdyozp0007jbul3o4oqoox",
              "createdAt": "2022-08-09T19:50:25.003Z",
              "updatedAt": "2022-08-09T19:50:25.019Z",
              "fields": [
                {
                  "id": "cl6mlkik800010glagppvr24i",
                  "label": "Property",
                  "type": "NUMBER",
                  "templateId": "cl6mlkijv00000glajl1xljzl",
                  "createdAt": "2022-08-09T19:50:25.003Z",
                  "updatedAt": "2022-08-09T19:50:25.019Z"
                }
              ]
            },
            "dataUpdateCount": 1,
            "dataUpdatedAt": 1660643358372,
            "error": null,
            "errorUpdateCount": 0,
            "errorUpdatedAt": 0,
            "fetchFailureCount": 0,
            "fetchMeta": null,
            "isInvalidated": false,
            "status": "success",
            "fetchStatus": "idle"
          },
          "queryKey": [
            "templates",
            "cl6mlkijv00000glajl1xljzl"
          ],
          "queryHash": "[\"templates\",\"cl6mlkijv00000glajl1xljzl\"]"
        }
      ]
    }
    

Differences

To simplify finding out what's going on, this is the diff between the two dehydratedStates

@@ -23,7 +23,7 @@
           }
         ],
         "dataUpdateCount": 1,
-        "dataUpdatedAt": 1660642973913,
+        "dataUpdatedAt": 1660643358366,
         "error": null,
         "errorUpdateCount": 0,
         "errorUpdatedAt": 0,
@@ -37,6 +37,42 @@
         "templates"
       ],
       "queryHash": "[\"templates\"]"
+    },
+    {
+      "state": {
+        "data": {
+          "id": "cl6mlkijv00000glajl1xljzl",
+          "label": "foo",
+          "ownerId": "cl6cdyozp0007jbul3o4oqoox",
+          "createdAt": "2022-08-09T19:50:25.003Z",
+          "updatedAt": "2022-08-09T19:50:25.019Z",
+          "fields": [
+            {
+              "id": "cl6mlkik800010glagppvr24i",
+              "label": "Property",
+              "type": "NUMBER",
+              "templateId": "cl6mlkijv00000glajl1xljzl",
+              "createdAt": "2022-08-09T19:50:25.003Z",
+              "updatedAt": "2022-08-09T19:50:25.019Z"
+            }
+          ]
+        },
+        "dataUpdateCount": 1,
+        "dataUpdatedAt": 1660643358372,
+        "error": null,
+        "errorUpdateCount": 0,
+        "errorUpdatedAt": 0,
+        "fetchFailureCount": 0,
+        "fetchMeta": null,
+        "isInvalidated": false,
+        "status": "success",
+        "fetchStatus": "idle"
+      },
+      "queryKey": [
+        "templates",
+        "cl6mlkijv00000glajl1xljzl"
+      ],
+      "queryHash": "[\"templates\",\"cl6mlkijv00000glajl1xljzl\"]"
     }
   ]
 }

The timestamp difference of dataUpdatedAt is just the cache "updated at" timestamp, not the actual "updated at" from the database object.

dgrcode avatar Aug 16 '22 10:08 dgrcode

Hey @dgrcode, thanks for reporting this! Seems like something we should look into. Reading through the lengths of this, I think it'd be very helpful to have a failing reproduction test for this. Could you maybe reproduce this as a failing test in https://github.com/blitz-js/superjson/blob/main/src/index.test.ts, and open a PR with it? That'd allow for easier fixing, I think.

Skn0tt avatar Aug 16 '22 11:08 Skn0tt

Sure! I'll get to it as soon as possible. That's hopefully later today or tomorrow 🤞

Thanks for replying so fast :)

dgrcode avatar Aug 16 '22 11:08 dgrcode