56

I started out with the relay-starter-kit and also worked my way through the Relay and GraphQL documentation. But there are quite a few areas that are unexplained and mysterious.

Seriously I read a lot of documentations everywhere about all these things but couldn't find any satisfying explanations for the following questions:

What is this for? I put logging but it never even gets called at all:

var {nodeInterface, nodeField} = nodeDefinitions(
  (globalId) => {
    var {type, id} = fromGlobalId(globalId);
    if (type === 'User') {
      return getUser(id);
    } else if (type === 'Widget') {
      return getWidget(id);
    } else {
      return null;
    }
  },
  (obj) => {
    if (obj instanceof User) {
      return userType;
    } else if (obj instanceof Widget) {
      return widgetType;
    } else {
      return null;
    }
  }
);

And what is the actual effect of this:

interfaces: [nodeInterface],

Maybe related to that, what does the node field here do:

var queryType = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    node: nodeField,
    // Add your own root fields here
    viewer: {
      type: userType,
      resolve: () => getViewer(),
    },
  }),
});

And what is the magic around the id field? What is globalIdField for?

I have an id in my database and thought I could use it in my GraphQL objects:

Instead of:

id: globalIdField('User'),

I want to use my database id:

id: {
  type: GraphQLID,
  description: 'The identifier'
},

But if I do that I get an error in the browser saying RelayQueryWriter: Could not find a type name for record '1'.

I can get rid of that error by adding __typename to my component containers Relay Query but that seems all wrong.

It would be great if you could give some deeper insides and a better explanation here and enhance the official documentation.

Thank you

jonathancardoso
  • 11,737
  • 7
  • 53
  • 72
Christine
  • 3,014
  • 2
  • 24
  • 34

1 Answers1

58

The Node root field, in combination with globally unique IDs, comes into play when Relay needs to refetch an object. Refetching occurs when you call this.props.relay.forceFetch() or when you add fields to the query for an object whose global ID is known because it has already been partially fetched.

In cases like these, Relay will short circuit the regular query and execute a query for the object(s) directly using its global ID and the node root call.

Example:

Assume that $showComments was false when this query was first resolved.

query {
  viewer {
    stories(first: 10) {
      edges {
        node {
          id,
          comments(first: 10) @include(if: $showComments) { 
            author, 
            commentText 
          }
          text,
        }
      }
    }
  }
}

This will have caused a fetch for id and text for some number of stories, whose IDs are now known.

Imagine that at some future time, the variable $showComments became true. Relay will refetch only the data it needs using the node root field.

query {
  node(id: "ABC123") { 
    fragment on Story { comments(first: 10) { author, commentText } }
  }
  node(id: "DEF456") { 
    fragment on Story { comments(first: 10) { author, commentText } }
  }
  node(id: "GHI789") { 
    fragment on Story { comments(first: 10) { author, commentText } }
  }
  ...
}

This depends on a few pieces:

  1. Each object must have a globally unique ID, or be identified by a type/ID pair (the globalIdField helper does this and produces a base64 encoded string).
  2. The server must know how to resolve an object from a globally unique ID, and vice versa. This is what the nodeDefinitions are for.
  3. Any object that hopes to be refetchable using this system must implement the nodeInterface.

See also: https://relay.dev/docs/guides/graphql-server-specification/#object-identification

steveluscher
  • 4,144
  • 24
  • 42
  • 2
    Hopefully this will make its way into the tutorial soon. I was also confused by this for quite a while until I finally worked through everything and put the pieces together. – Dmitry Minkovsky Nov 05 '15 at 16:30
  • 3
    Maybe add a bit more to this answer regarding `fromGlobalId` and `toGlobalId`? and how they facilitate refetching of any object type? Reading https://github.com/graphql/graphql-relay-js/blob/master/src/node/node.js and other files in that repo helped me a lot, but distilling that stuff into words would have saved me lots of time. – Dmitry Minkovsky Nov 05 '15 at 16:34
  • For a concise (you can skip to the end for the summary) & code-illustrated explanation of what the mysterious nodeInterface, nodeField, globalFieldId are, perhaps https://medium.com/p/relay-graphql-de-mystifying-node-id-38757121b9c – nethsix Apr 09 '16 at 02:54
  • 1
    @steveluscher But this will cause to refetch `stories` data again. What if I just want the `comments`? I couldn't find any real world example with `nodeDefinitions`. Or I am missing something? – user6227254 Nov 27 '16 at 18:18
  • @user6227254 You'll only fetch the first 10 comments of each story. The remaining fields of the story aren't fetched since they are not included in the node query. – John Barela Dec 28 '16 at 03:09
  • 1
    This sentence "Relay will refetch only the data it needs using the node root field." explained everything to me. Thanks a lot! – gpbaculio Mar 17 '17 at 19:30
  • Updated the URL for you, cglacet. It’s the server’s responsibility. – steveluscher Mar 16 '21 at 18:56