Q:

How does a GraphQL mutation know what node it's modifying? Is there a unique and global node ID for each node in the database?

I'm learning GraphQL through Robin Wieruch's helpful tutorial (https://www.robinwieruch.de/getting-started-github-graphql-api/) 
One thing I'm unclear about is how a mutation for a particular endpoint "knows" what object it's modifying (queries seem to work by traversing the DB tree from top to bottom).  
For instance, with the GitHub GraphQL API addReaction mutation (https://developer.github.com/v4/mutation/addreaction/), we have the subjectID, which the docs say is "The Node ID of the subject to modify.".  OK, but presumably there might be more than one thing in the entirety of GitHub to which one might want to add a reaction.  Does this mean that this subjectID is unique across the entirety of GitHub, so that the GraphQL API knows 'automagically' exactly where in all of the many elements in GitHub it should add (for example) a 'thumbs up'?  If that's not the case, how does it know?  If it is the case, does this unique and global ID exist permanently, up until the node is deleted?  

A:

GitHub's GraphQL schema is Relay-compliant. From their homepage:

Relay is a JavaScript framework for building data-driven React applications powered by GraphQL, designed from the ground up to be easy to use, extensible and, most of all, performant.

While Relay is a front-end client, it requires schemas to include certain features in order for the client to work as expected. One of these features is Global Object Identification. How you provide an id that's globally unique is left up to the server. However, Relay provides a server-side helper library that has a built-in mechanism:
export function toGlobalId(type: string, id: string): string {
  return base64([type, id].join(':'));
}

export function fromGlobalId(globalId: string): ResolvedGlobalId {
  const unbasedGlobalId = unbase64(globalId);
  const delimiterPos = unbasedGlobalId.indexOf(':');
  return {
    type: unbasedGlobalId.substring(0, delimiterPos),
    id: unbasedGlobalId.substring(delimiterPos + 1),
  };
}

All this does is combine the id and the GraphQL type and then encodes it using Base64. As long as you're not returning rows from different tables as the the same type, this is guaranteed to be unique.
So that's how GitHub's API works. However, not all GraphQL APIs are Relay-compliant. There's both benefits and costs to designing a schema that way. To answer your question more directly, how a field is resolved is entirely up to the server. The resolver you write for a particular field (including root-level mutations) is passed in a couple of pieces of information:

the value the parent field resolved to
the arguments for the field

Most implementations also provide some kind of context object and an info object that describes the GraphQL request itself, although this is not strictly part of the spec. The point is, that's all the resolver is really "aware" of. However, since you're the one writing the resolver, you know what data needs to be queried or manipulated based on the field you're writing the resolver for.

