Global Object Identification
https://graphql.org/learn/global-object-identification/
일관된 개체 액세스를 통해 간단한 캐싱 및 개체 조회 가능1)
GraphQL 클라이언트가 캐싱 및 데이터 다시 가져오기를 우아하게 처리할 수 있는 옵션을 제공하려면 GraphQL 서버는 표준화된 방식으로 개체 식별자를 노출해야 합니다.2)
이것이 작동하려면 클라이언트가 ID로 개체를 요청하는 표준 메커니즘을 통해 쿼리해야 합니다. 그런 다음 응답에서 스키마는 이러한 ID를 제공하는 표준 방법을 제공해야 합니다.3)
개체에 대해 ID 외에 알려진 것이 거의 없기 때문에 이러한 개체를 “노드”라고 합니다. 다음은 노드에 대한 쿼리의 예입니다.4)
{ node(id: "4") { id ... on User { name } }
- GraphQL 스키마는 루트 쿼리 개체의 노드 필드를 통해 모든 개체를 가져올 수 있도록 형식이 지정됩니다. 이것은 “Node” 인터페이스를 준수하는 객체를 반환합니다.5)
- id 필드는 응답에서 안전하게 추출할 수 있으며 캐싱 및 다시 가져오기를 통해 재사용을 위해 저장할 수 있습니다.6)
- 클라이언트는 인터페이스 조각을 사용하여 노드 인터페이스를 준수하는 유형에 특정한 추가 정보를 추출할 수 있습니다. 이 경우에는 “User” 입니다.7)
노드 인터페이스는 다음과 같습니다.8)
# An object with a Globally Unique ID interface Node { # The ID of the object. id: ID! }
사용자가 다음을 통해 준수하는 경우:9)
type User implements Node { id: ID! # Full name name: String! }
Specification
아래의 모든 내용은 서버 구현 간의 일관성을 보장하기 위해 개체 식별에 대한 사양을 보다 공식적인 요구 사항과 함께 설명합니다. 이러한 사양은 서버가 릴레이 API 클라이언트와 호환되는 방법을 기반으로 하지만 모든 클라이언트에 유용할 수 있습니다.10)
Reserved Types
이 사양과 호환되는 GraphQL 서버는 일관된 개체 식별 모델을 지원하기 위해 특정 유형 및 유형 이름을 예약해야 합니다. 특히 이 사양은 다음 유형에 대한 지침을 만듭니다.11)
Node Interface
서버는 Node라는 인터페이스를 제공해야 합니다. 해당 인터페이스는 null이 아닌 ID를 반환하는 id라는 필드를 정확히 하나만 포함해야 합니다.14)
이 ID는 이 개체에 대한 전역적으로 고유한 식별자여야 하며 이 ID만 주어지면 서버가 개체를 다시 가져올 수 있어야 합니다.15)
Introspection
위의 인터페이스를 올바르게 구현하는 서버는 다음 자체 검사 쿼리를 수락하고 제공된 응답을 반환합니다.16)
{ __type(name: "Node") { name kind fields { name type { kind ofType { name kind } } } } }
yields
{ "__type": { "name": "Node", "kind": "INTERFACE", "fields": [ { "name": "id", "type": { "kind": "NON_NULL", "ofType": { "name": "ID", "kind": "SCALAR" } } } ] } }
Node root field
서버는 노드 인터페이스를 반환하는 노드라는 루트 필드를 제공해야 합니다. 이 루트 필드는 id라는 null이 아닌 ID인 정확히 하나의 인수를 취해야 합니다.17)
쿼리가 노드를 구현하는 개체를 반환하는 경우 노드의 id 필드에 서버가 반환한 값이 노드 루트 필드에 id 매개변수로 전달될 때 이 루트 필드는 동일한 개체를 다시 가져와야 합니다.18)
서버는 이 데이터를 가져오기 위해 최선을 다해야 하지만 항상 가능한 것은 아닙니다. 예를 들어, 서버는 유효한 ID를 가진 사용자를 반환할 수 있지만 노드 루트 필드로 해당 사용자를 다시 가져오도록 요청하면 사용자의 데이터베이스를 사용할 수 없거나 사용자가 계정을 삭제했을 수 있습니다. 이 경우 이 필드를 쿼리한 결과는 null이어야 합니다.19)
Introspection
위의 요구 사항을 올바르게 구현하는 서버는 다음 자체 검사 쿼리를 수락하고 제공된 응답이 포함된 응답을 반환합니다.20)
{ __schema { queryType { fields { name type { name kind } args { name type { kind ofType { name kind } } } } } } }
yields
{ "__schema": { "queryType": { "fields": [ // This array may have other entries { "name": "node", "type": { "name": "Node", "kind": "INTERFACE" }, "args": [ { "name": "id", "type": { "kind": "NON_NULL", "ofType": { "name": "ID", "kind": "SCALAR" } } } ] } ] } } }
Field stability
동일한 ID로 Node를 구현하는 두 개체가 쿼리에 나타나면 두 개체가 같아야 합니다.21)
이 정의의 목적을 위해 객체 평등은 다음과 같이 정의됩니다.22)
- 두 개체에서 필드를 쿼리하는 경우 첫 번째 개체에서 해당 필드를 쿼리한 결과는 두 번째 개체에서 해당 필드를 쿼리한 결과와 같아야 합니다.23)
예를 들어:27)
{ fourNode: node(id: "4") { id ... on User { name userWithIdOneGreater { id name } } } fiveNode: node(id: "5") { id ... on User { name userWithIdOneLess { id name } } } }
다음을 반환할 수 있습니다.28)
{ "fourNode": { "id": "4", "name": "Mark Zuckerberg", "userWithIdOneGreater": { "id": "5", "name": "Chris Hughes" } }, "fiveNode": { "id": "5", "name": "Chris Hughes", "userWithIdOneLess": { "id": "4", "name": "Mark Zuckerberg", } } }
fourNode.id와 fiveNode.userWithIdOneLess.id가 동일하기 때문에 우리는 상기 fourNode.name이 fiveNode.userWithIdOneLess.name과 동일해야 하고 실제로 동일해야 한다는 위의 조건에 의해 보장됩니다.29)
Plural identifying root fields
사용자 이름을 가져와 해당 사용자를 반환하는 username이라는 루트 필드를 상상해 보십시오.30)
{ username(username: "zuck") { id } }
다음을 반환할 수 있습니다.31)
{ "username": { "id": "4", } }
분명히, 우리는 ID 4를 가진 사용자인 응답의 객체를 사용자 이름 “zuck”으로 식별하는 요청과 연결할 수 있습니다. 이제 사용자 이름 목록을 가져와 객체 목록을 반환하는 사용자 이름이라는 루트 필드를 상상해 보십시오.32)
{ usernames(usernames: ["zuck", "moskov"]) { id } }
다음을 반환할 수 있습니다.33)
{ "usernames": [ { "id": "4", }, { "id": "6" } ] }
클라이언트가 사용자 이름을 응답에 연결할 수 있으려면 응답의 배열이 인수로 전달된 배열과 크기가 같고 응답의 순서가 인수의 순서와 일치해야 함을 알아야 합니다. . 우리는 이러한 복수의 식별 루트 필드를 호출하며 해당 요구 사항은 아래에 설명되어 있습니다.34)
Fields
이 사양을 준수하는 서버는 입력 인수 목록을 수락하고 응답 목록을 반환하는 루트 필드를 노출할 수 있습니다. 사양 준수 클라이언트가 이러한 필드를 사용하려면 이러한 필드가 루트 필드를 식별하는 복수이어야 하며 다음 요구 사항을 준수해야 합니다.35)
참고 사양 준수 서버는 루트 필드를 식별하는 복수가 아닌 루트 필드를 노출할 수 있습니다. 사양 준수 클라이언트는 해당 필드를 쿼리의 루트 필드로 사용할 수 없습니다.36)
복수의 식별 루트 필드에는 단일 인수가 있어야 합니다. 해당 인수의 유형은 null이 아닌 null이 아닌 목록이어야 합니다. 사용자 이름 예제에서 필드는 사용자 이름이라는 단일 인수를 사용하며, 유형(유형 시스템 약칭 사용)은 [String!]!
이 됩니다.37)
복수 식별 루트 필드의 반환 유형은 목록이거나 목록 주위의 null이 아닌 래퍼여야 합니다. 목록은 Node 인터페이스, Node 인터페이스를 구현하는 객체 또는 이러한 유형 주위의 null이 아닌 래퍼를 래핑해야 합니다.38)
복수 식별 루트 필드가 사용될 때마다 응답의 목록 길이는 인수의 목록 길이와 같아야 합니다. 응답의 각 항목은 입력의 항목과 일치해야 합니다. 더 공식적으로, 루트 필드에 입력 목록 Lin을 전달하면 출력 값 Lout이 생성되고 임의의 순열 P에 대해 루트 필드 P(Lin)을 전달하면 출력 값 P(Lout)이 생성되어야 합니다.39)
이 때문에 서버는 응답 유형이 null이 아닌 래퍼를 래핑하지 않는 것이 좋습니다. 입력에서 주어진 항목에 대한 개체를 가져올 수 없는 경우에도 해당 입력 항목에 대한 출력에 값을 제공해야 하기 때문입니다. ; null은 그렇게 하는 데 유용한 값입니다.40)