{"componentChunkName":"component---src-templates-doc-tsx","path":"/learn/global-object-identification/","result":{"data":{"doc":{"frontmatter":{"title":"Global Object Identification","date":null,"permalink":"/learn/global-object-identification/","byline":null,"guestBio":null,"sublinks":null,"layout":"docs"},"id":"62587b8a-8864-5487-b7db-8aacf4d0e464","rawMarkdownBody":"\n> Consistent object access enables simple caching and object lookups\n\nTo provide options for GraphQL clients to elegantly handle caching and data \nrefetching, GraphQL servers need to expose object identifiers in a standardized \nway. \n\nFor this to work, a client will need to query via a standard mechanism to \nrequest an object by ID. Then, in the response, the schema will need to provide a \nstandard way of providing these IDs.\n\nBecause little is known about the object other than its ID, we call these\nobjects \"nodes.\" Here is an example query for a node:\n\n```graphql\n{\n  node(id: \"4\") {\n    id\n    ... on User {\n      name\n    }\n  }\n```\n\n- The GraphQL schema is formatted to allow fetching any object via the `node` field on the root query object. This\n  returns objects which conform to a \"Node\" [interface](/learn/schema/#interfaces).\n- The `id` field can be extracted out of the response safely, and can be stored for re-use via caching and refetching.\n- Clients can use interface fragments to extract additional information specific to the type which conform to the node interface. In this case a \"User\".\n\nThe Node interface looks like:\n\n```graphql\n# An object with a Globally Unique ID\ninterface Node {\n  # The ID of the object.\n  id: ID!\n}\n```\n\nWith a User conforming via:\n\n```graphql\ntype User implements Node {\n  id: ID!\n  # Full name\n  name: String!\n}\n```\n\n# Specification\n\nEverything below describes with more formal requirements a specification around object \nidentification in order to conform to ensure consistency across server implementations. These \nspecifications are based on how a server can be compliant with the [Relay][relay] API client, but \ncan be useful for any client.\n\n# Reserved Types\n\nA GraphQL server compatible with this spec must reserve certain types and type names\nto support the consistent object identification model. In particular, this spec creates \nguidelines for the following types:\n\n - An interface named `Node`.\n - The `node` field on the root query type.\n\n# Node Interface\n\nThe server must provide an interface called `Node`. That interface\nmust include exactly one field, called `id` that returns a non-null `ID`.\n\nThis `id` should be a globally unique identifier for this object, and given\njust this `id`, the server should be able to refetch the object.\n\n## Introspection\n\nA server that correctly implements the above interface will accept the following\nintrospection query, and return the provided response:\n\n```graphql\n{\n  __type(name: \"Node\") {\n    name\n    kind\n    fields {\n      name\n      type {\n        kind\n        ofType {\n          name\n          kind\n        }\n      }\n    }\n  }\n}\n```\n\nyields\n\n```json\n{\n  \"__type\": {\n    \"name\": \"Node\",\n    \"kind\": \"INTERFACE\",\n    \"fields\": [\n      {\n        \"name\": \"id\",\n        \"type\": {\n          \"kind\": \"NON_NULL\",\n          \"ofType\": {\n            \"name\": \"ID\",\n            \"kind\": \"SCALAR\"\n          }\n        }\n      }\n    ]\n  }\n}\n```\n\n# Node root field\n\nThe server must provide a root field called `node` that returns the `Node`\ninterface. This root field must take exactly one argument, a non-null ID\nnamed `id`.\n\nIf a query returns an object that implements `Node`, then this root field\nshould refetch the identical object when value returned by the server in the\n`Node`'s `id` field is passed as the `id` parameter to the `node` root field.\n\nThe server must make a best effort to fetch this data, but it may not always\nbe possible; for example, the server may return a `User` with a valid `id`,\nbut when the request is made to refetch that user with the `node` root field,\nthe user's database may be unavailable, or the user may have deleted their\naccount. In this case, the result of querying this field should be `null`.\n\n## Introspection\n\nA server that correctly implements the above requirement will accept the\nfollowing introspection query, and return a response that contains the\nprovided response.\n\n```graphql\n{\n  __schema {\n    queryType {\n      fields {\n        name\n        type {\n          name\n          kind\n        }\n        args {\n          name\n          type {\n            kind\n            ofType {\n              name\n              kind\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nyields\n\n```json\n{\n  \"__schema\": {\n    \"queryType\": {\n      \"fields\": [\n        // This array may have other entries\n        {\n          \"name\": \"node\",\n          \"type\": {\n            \"name\": \"Node\",\n            \"kind\": \"INTERFACE\"\n          },\n          \"args\": [\n            {\n              \"name\": \"id\",\n              \"type\": {\n                \"kind\": \"NON_NULL\",\n                \"ofType\": {\n                  \"name\": \"ID\",\n                  \"kind\": \"SCALAR\"\n                }\n              }\n            }\n          ]\n        }\n      ]\n    }\n  }\n}\n```\n\n# Field stability\n\nIf two objects appear in a query, both implementing `Node` with identical\nIDs, then the two objects must be equal.\n\nFor the purposes of this definition, object equality is defined as follows:\n\n - If a field is queried on both objects, the result of querying that field on\nthe first object must be equal to the result of querying that field on the\nsecond object.\n   - If the field returns a scalar, equality is defined as is appropriate for\n   that scalar.\n   - If the field returns an enum, equality is defined as both fields returning\n   the same enum value.\n   - If the field returns an object, equality is defined recursively as per the\n   above.\n\nFor example:\n\n```graphql\n{\n  fourNode: node(id: \"4\") {\n    id\n    ... on User {\n      name\n      userWithIdOneGreater {\n        id\n        name\n      }\n    }\n  }\n  fiveNode: node(id: \"5\") {\n    id\n    ... on User {\n      name\n      userWithIdOneLess {\n        id\n        name\n      }\n    }\n  }\n}\n```\n\nmight return:\n\n```json\n{\n  \"fourNode\": {\n    \"id\": \"4\",\n    \"name\": \"Mark Zuckerberg\",\n    \"userWithIdOneGreater\": {\n      \"id\": \"5\",\n      \"name\": \"Chris Hughes\"\n    }\n  },\n  \"fiveNode\": {\n    \"id\": \"5\",\n    \"name\": \"Chris Hughes\",\n    \"userWithIdOneLess\": {\n      \"id\": \"4\",\n      \"name\": \"Mark Zuckerberg\",\n    }\n  }\n}\n```\n\nBecause `fourNode.id` and `fiveNode.userWithIdOneLess.id` are the same, we are\nguaranteed by the conditions above that `fourNode.name` must be the same as\n`fiveNode.userWithIdOneLess.name`, and indeed it is.\n\n# Plural identifying root fields\n\nImagine a root field named `username`, that takes a user's username and\nreturns the corresponding user:\n\n```graphql\n{\n  username(username: \"zuck\") {\n    id\n  }\n}\n```\n\nmight return:\n\n```json\n{\n  \"username\": {\n    \"id\": \"4\",\n  }\n}\n```\n\nClearly, we can link up the object in the response, the user with ID 4,\nwith the request, identifying the object with username \"zuck\". Now imagine a\nroot field named `usernames`, that takes a list of usernames and returns a\nlist of objects:\n\n\n```graphql\n{\n  usernames(usernames: [\"zuck\", \"moskov\"]) {\n    id\n  }\n}\n```\n\nmight return:\n\n```json\n{\n  \"usernames\": [\n    {\n      \"id\": \"4\",\n    },\n    {\n      \"id\": \"6\"\n    }\n  ]\n}\n```\n\nFor clients to be able to link the usernames to the responses, it needs to\nknow that the array in the response will be the same size as the array\npassed as an argument, and that the order in the response will match the\norder in the argument. We call these *plural identifying root fields*, and\ntheir requirements are described below.\n\n## Fields\n\nA server compliant with this spec may expose root fields that accept a list of input\narguments, and returns a list of responses. For spec-compliant clients to use these fields,\nthese fields must be *plural identifying root fields*, and obey the following\nrequirements.\n\nNOTE Spec-compliant servers may expose root fields that are not *plural\nidentifying root fields*; the spec-compliant client will just be unable to use those\nfields as root fields in its queries.\n\n*Plural identifying root fields* must have a single argument. The type of that\nargument must be a non-null list of non-nulls. In our `usernames` example, the\nfield would take a single argument named `usernames`, whose type (using our type\nsystem shorthand) would be `[String!]!`.\n\nThe return type of a *plural identifying root field* must be a list, or a\nnon-null wrapper around a list. The list must wrap the `Node` interface, an\nobject that implements the `Node` interface, or a non-null wrapper around\nthose types.\n\nWhenever the *plural identifying root field* is used, the length of the\nlist in the response must be the same as the length of the list in the\narguments. Each item in the response must correspond to its item in the input;\nmore formally, if passing the root field an input list `Lin` resulted in output\nvalue `Lout`, then for an arbitrary permutation `P`, passing the root field\n`P(Lin)` must result in output value `P(Lout)`.\n\nBecause of this, servers are advised to not have the response type\nwrap a non-null wrapper, because if it is unable to fetch the object for\na given entry in the input, it still must provide a value in the output\nfor that input entry; `null` is a useful value for doing so.\n\n[relay]: https://facebook.github.io/relay/\n"},"nextDoc":{"frontmatter":{"title":"Caching","permalink":"/learn/caching/"}}},"pageContext":{"permalink":"/learn/global-object-identification/","nextPermalink":"/learn/caching/","sideBarData":[{"name":"Learn","links":[{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Introduction.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Introduction to GraphQL","permalink":"/learn/","next":"/learn/queries/","category":"Learn","sublinks":null,"sidebarTitle":"Introduction","date":null},"id":"7106674b-2f21-5d84-b8c3-c89c7e2b5561"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Learn-Queries.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Queries and Mutations","permalink":"/learn/queries/","next":"/learn/schema/","category":"Learn","sublinks":"Fields,Arguments,Aliases,Fragments,Operation Name,Variables,Directives,Mutations,Inline Fragments","sidebarTitle":null,"date":null},"id":"d9731a25-3fbf-5586-ae79-95daca042058"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Learn-Schema.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Schemas and Types","permalink":"/learn/schema/","next":"/learn/validation/","category":"Learn","sublinks":"Type System,Type Language,Object Types and Fields,Arguments,The Query and Mutation Types,Scalar Types,Enumeration Types,Lists and Non-Null,Interfaces,Union Types,Input Types","sidebarTitle":null,"date":null},"id":"6a9e1e57-c0ff-542e-a525-c28f658ad0a7"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Learn-Validation.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Validation","permalink":"/learn/validation/","next":"/learn/execution/","category":"Learn","sublinks":null,"sidebarTitle":null,"date":null},"id":"f69c6420-bf79-56a6-b7af-09fbe09f7565"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Learn-Execution.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Execution","permalink":"/learn/execution/","next":"/learn/introspection/","category":"Learn","sublinks":null,"sidebarTitle":null,"date":null},"id":"4d6ab3f1-c361-55eb-9800-797f90c3d8f6"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/Learn-Introspection.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Introspection","permalink":"/learn/introspection/","next":"/learn/best-practices/","category":"Learn","sublinks":null,"sidebarTitle":null,"date":null},"id":"2911ba27-f601-585a-a70f-3ad2cb78f9f3"}]},{"name":"Best Practices","links":[{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-Introduction.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"GraphQL Best Practices","permalink":"/learn/best-practices/","next":"/learn/thinking-in-graphs/","category":"Best Practices","sublinks":null,"sidebarTitle":"Introduction","date":null},"id":"2f5369f2-a0d4-5f71-972e-6569092db7a4"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-ThinkingInGraphs.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Thinking in Graphs","permalink":"/learn/thinking-in-graphs/","next":"/learn/serving-over-http/","category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"692af90e-5600-50bd-bdd7-9048bc299512"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-ServingOverHTTP.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Serving over HTTP","permalink":"/learn/serving-over-http/","next":"/learn/authorization/","category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"fbee8a12-2a83-51c2-b7f3-068438a89b9b"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-Authorization.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Authorization","permalink":"/learn/authorization/","next":"/learn/pagination/","category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"2439b6ee-f2a2-5bbe-97d2-0f185a0b4aa8"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-Pagination.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Pagination","permalink":"/learn/pagination/","next":"/learn/global-object-identification/","category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"5602cb49-7976-5673-8868-32155bd8b94e"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-NodeInterface.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Global Object Identification","permalink":"/learn/global-object-identification/","next":"/learn/caching/","category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"62587b8a-8864-5487-b7db-8aacf4d0e464"},{"fileAbsolutePath":"/opt/build/repo/src/content/learn/BestPractice-Caching.md","parent":{"relativeDirectory":"learn","sourceInstanceName":"content"},"frontmatter":{"title":"Caching","permalink":"/learn/caching/","next":null,"category":"Best Practices","sublinks":null,"sidebarTitle":null,"date":null},"id":"64807002-e363-5637-aafc-4af37aa77f71"}]}],"sourcePath":"src/content/learn/BestPractice-NodeInterface.md"}},"staticQueryHashes":["1581580458"]}