Relay: [Modern] RelayResponseNormalizer: Expected id of elements of field `nodes` to be strings.

Created on 22 Apr 2017  路  10Comments  路  Source: facebook/relay

Getting the following error:

RelayResponseNormalizer: Expected id of elements of field 'nodes' to be strings.

Query:

query wubQuery {
  allWub {
    nodes {
      id
      wubwub
    }
  }
}

Schema:

type Wub {
  nodeId: ID!
  id: Int!
  wubwub: String!
  ...
}

API Response:

{
  "data": {
    "allWub": {
      "nodes": [
        {
          "id": 1,
          "wubwub": "wub1"
        },
        {
          "id": 2,
          "wubwub": "wub2"
        }
      ]
    }
  }
}

Most helpful comment

As first sight, the workaround from @markymc look promising
However, I run into weird situation that cause problem when convert from int to string that I never think I will run into

My solution: Use alias for id field
Modify your query like below

whateverId: id

and use whateverId instead of id

All 10 comments

Yeah we need to document this. Currently id is treated as a reserved word and must be a globablly unique value.

Maybe change the call sites to say global unique id instead of strings might help when getting these errors

Some context, as I understand it -

Most GraphQL data at Facebook implements the Node interface. This means that whatever the data object is - person, page, location, hobby, or wub - it can be fetched from a single endpoint like node(id: "4ugbgvjesjerwuerth") through a globally unique ID for the object. This globalId is unique across all "databases" and "tables" and "objects" across your domain, and can be generated like $globalId = base64("$ObjectName:$ObjectPK"). Relay uses this node() endpoint to power its caching mechanism - regardless of where in the query the object was originally loaded, when you ask Relay to refetch/add new fields, it will hit the topmost node() endpoint. Therefore, it expects all id fields to be compliant with this global NodeInterface implementation, which required it to be a globally unique string. On the server side, each object has to explicitly to declare whether it supports NodeInterface, so on the client side Relay would just ignore id fields for objects who did not enroll into that contract. I think this is how it was implemented in Relay classic.

Temporary workaround: alias id to _id (Note single underscore - don't use double underscore since such fields are reserved for the GraphQL spec)

You need an interface Node that takes an ID, something like

interface Node {
id: ID!
}

and then use the interface in your type Wub schema like this,

type Wub implements Node {
id: ID!
wubwub: String!
}

Just ran into the same problem. I provided a global unique id as nodeId while maintaining the objectPK as id.

I had the same problem. My guess is that the graphql schema that I was connecting to was using Long! instead of ID!. Is my guess correct?

I'm sorry this issue has sat here for so long. In an attempt to clean up our issue queue we're closing some aging or unclear-action issues.

Sounds like there was some answers to this question. Relay contains this restriction on id fields.

I'm gonna leave this in here in case anyone else is having trouble with this when using Relay Modern and can't control their QraphQL endpoint for some reason:

My hacky solution to this problem was to rewrite the JSON response from the server before it gets passed through to the Relay layer.

I did this by writing a (not pure I know) function to find all id keys and stringify them.

  // This is needed because Relay Modern doesn't like `id` fields being `Int`s. 
  // Once we've updated the GraphQL endpoints on the server to adhere to the spec we can get rid of this.
  // See https://github.com/facebook/relay/issues/1682
  function stringifyIds(json) {
    for (const node in json) {
      if (node === 'id') {
        console.log("stringifying ID for: ", json)

        json[node] = json[node].toString()   
        continue 
      }

      if (typeof json[node] === 'object') {
        stringifyIds(json[node])
      }
    }
  }

This was used in the .then(response => ... part of my Relay environment's fetchquery:

```
// Define a function that fetches the results of an operation (query/mutation/etc)
// and returns its results as a Promise:
function fetchQuery(operation, variables, cacheConfig, uploadables) {
return fetch('https://graph.iflyer.tv', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
}, // Add authentication and other headers here
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
}).then(response => {
const parsedJson = JSON.parse(response._bodyText)
stringifyIds(parsedJson)
response._bodyText = JSON.stringify(parsedJson)

  return response.json();
});

}

As first sight, the workaround from @markymc look promising
However, I run into weird situation that cause problem when convert from int to string that I never think I will run into

My solution: Use alias for id field
Modify your query like below

whateverId: id

and use whateverId instead of id

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leebyron picture leebyron  路  3Comments

rayronvictor picture rayronvictor  路  3Comments

derekdowling picture derekdowling  路  3Comments

janicduplessis picture janicduplessis  路  3Comments

amccloud picture amccloud  路  3Comments