Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Modern] RelayResponseNormalizer: Expected id of elements of field nodes to be strings. #1682

Closed
kaminsod opened this issue Apr 22, 2017 · 10 comments

Comments

@kaminsod
Copy link

kaminsod commented Apr 22, 2017

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"
        }
      ]
    }
  }
}
@josephsavona
Copy link
Contributor

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

@edvinerikson
Copy link
Contributor

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

@eugene1g
Copy link

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.

@josephsavona
Copy link
Contributor

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

@saudpunjwani101
Copy link

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!
}

@Felix-N
Copy link

Felix-N commented May 2, 2017

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

@arvinsim
Copy link

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?

@leebyron
Copy link
Contributor

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.

@markymc
Copy link

markymc commented Nov 9, 2017

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();
    });
  }

@MinhThienDX
Copy link

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

steverice added a commit to pinterest/graphql-lint-rules that referenced this issue Mar 7, 2023
In Relay, `id` is [treated as a reserved word and must be a globally unique value](facebook/relay#1682 (comment)).
That means when an `id` field is present on a type, it must be the type that Relay expects (a string type).

This linter assumes that an `ID` scalar is present in the schema, and checks that if an `id` field is present, it is using that type.
steverice added a commit to pinterest/graphql-lint-rules that referenced this issue Mar 7, 2023
In Relay, `id` is [treated as a reserved word and must be a globally unique value](facebook/relay#1682 (comment)).
That means when an `id` field is present on a type, it must be the type that Relay expects (a string type).

This linter assumes that an `ID` scalar is present in the schema, and checks that if an `id` field is present, it is using that type.
github-actions bot pushed a commit to pinterest/graphql-lint-rules that referenced this issue Mar 7, 2023
## [2.2.0](v2.1.0...v2.2.0) (2023-03-07)

### Features

* **rule:** Add rule to check that `id` is only used for Relay IDs ([2f56901](2f56901)), closes [/github.com/facebook/relay/issues/1682#issuecomment-296393416](https://github.com/pinterest//github.com/facebook/relay/issues/1682/issues/issuecomment-296393416)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants