open:queries-and-mutations

Queries and Mutations

이 페이지에서는 GraphQL 서버를 쿼리하는 방법에 대해 자세히 알아볼 것입니다.1)

가장 간단하게 GraphQL은 객체의 특정 필드를 요청하는 것입니다. 매우 간단한 쿼리와 실행했을 때 얻은 결과부터 살펴보겠습니다.2)

{
  hero {
    name
  }
}

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

쿼리가 결과와 정확히 같은 모양을 하고 있음을 즉시 확인할 수 있습니다. 이것은 GraphQL에 필수적입니다. 왜냐하면 항상 예상한 대로 돌려받고 서버는 클라이언트가 요구하는 필드를 정확히 알고 있기 때문입니다.3)

필드 nameString 유형을 반환합니다. 이 경우 Star Wars의 주요 영웅인 "R2-D2"의 이름입니다.4)

아, 한 가지 더 - 위의 쿼리는 대화형입니다. 즉, 원하는 대로 변경하고 새 결과를 볼 수 있습니다. 쿼리의 영웅 개체에 appearsIn 필드를 추가하고 새 결과를 확인합니다.5)

이전 예에서 우리는 문자열을 반환한 영웅의 이름을 요청했지만 필드는 객체를 참조할 수도 있습니다. 이 경우 해당 개체에 대한 필드의 하위 선택을 만들 수 있습니다. GraphQL 쿼리는 관련 개체 및 해당 필드를 순회할 수 있으므로 클라이언트가 고전적인 REST 아키텍처에서 필요로 하는 여러 왕복을 만드는 대신 한 번의 요청으로 많은 관련 데이터를 가져올 수 있습니다.6)

{
  hero {
    name
    # Queries can have comments!
    friends {
      name
    }
  }
}

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

이 예에서 friends 필드는 항목 배열을 반환합니다. GraphQL 쿼리는 단일 항목 또는 항목 목록 모두에 대해 동일하게 보이지만 스키마에 표시된 내용을 기반으로 예상되는 항목을 알고 있습니다.7)

우리가 할 수 있는 유일한 것이 객체와 그 필드를 순회하는 것이라면 GraphQL은 이미 데이터 가져오기에 매우 유용한 언어가 될 것입니다. 그러나 필드에 인수를 전달하는 기능을 추가하면 상황이 훨씬 더 흥미로워집니다.8)

{
  human(id: "1000") {
    name
    height
  }
}

{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}

REST와 같은 시스템에서는 단일 인수 집합(요청의 쿼리 매개변수 및 URL 세그먼트)만 전달할 수 있습니다. 그러나 GraphQL에서는 모든 필드와 중첩 객체가 고유한 인수 집합을 얻을 수 있으므로 GraphQL을 여러 API 가져오기를 완전히 대체할 수 있습니다. 모든 클라이언트에서 개별적으로가 아니라 서버에서 한 번만 데이터 변환을 구현하기 위해 인수를 스칼라 필드에 전달할 수도 있습니다.9)

{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}

{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 5.6430448
    }
  }
}

인수는 다양한 유형이 될 수 있습니다. 위의 예에서는 유한한 옵션 세트(이 경우 길이 단위, METER 또는 FOOT) 중 하나를 나타내는 열거형을 사용했습니다. GraphQL은 기본 유형 세트와 함께 제공되지만 GraphQL 서버는 전송 형식으로 직렬화할 수 있는 한 자체 사용자 정의 유형을 선언할 수도 있습니다.10)

예리한 눈을 가진 경우 결과 개체 필드가 ​​쿼리의 필드 이름과 일치하지만 인수가 포함되지 않기 때문에 다른 인수를 사용하여 동일한 필드를 직접 쿼리할 수 없다는 것을 알았을 것입니다. 이것이 별칭이 필요한 이유입니다. 별칭을 사용하면 필드 결과의 이름을 원하는 이름으로 바꿀 수 있습니다.11)

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

위의 예에서 두 hero 필드는 충돌했지만 다른 이름으로 별칭을 지정할 수 있으므로 한 번의 요청으로 두 결과를 모두 얻을 수 있습니다.12)

앱에 상대적으로 복잡한 페이지가 있어서 두 명의 영웅을 친구와 함께 나란히 볼 수 있다고 가정해 보겠습니다. 이러한 쿼리가 빠르게 복잡해질 수 있다고 상상할 수 있습니다. 비교의 각 측면에 대해 필드를 한 번 이상 반복해야 하기 때문입니다.13)

이것이 GraphQL에 조각이라는 재사용 가능한 단위가 포함된 이유입니다. 조각을 사용하면 필드 집합을 구성한 다음 필요한 쿼리에 포함할 수 있습니다. 다음은 프래그먼트를 사용하여 위의 상황을 해결할 수 있는 방법의 예입니다.14)

{
  leftComparison: hero(episode: EMPIRE) {
    ...comparisonFields
  }
  rightComparison: hero(episode: JEDI) {
    ...comparisonFields
  }
}
 
fragment comparisonFields on Character {
  name
  appearsIn
  friends {
    name
  }
}

{
  "data": {
    "leftComparison": {
      "name": "Luke Skywalker",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "friends": [
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        },
        {
          "name": "C-3PO"
        },
        {
          "name": "R2-D2"
        }
      ]
    },
    "rightComparison": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

필드가 반복되면 위의 쿼리가 얼마나 반복되는지 알 수 있습니다. 프래그먼트의 개념은 복잡한 애플리케이션 데이터 요구 사항을 더 작은 청크로 분할하는 데 자주 사용됩니다. 특히 여러 UI 구성 요소를 서로 다른 프래그먼트를 사용하여 하나의 초기 데이터 가져오기로 결합해야 할 때 그렇습니다.15)

조각이 쿼리 또는 변형에 선언된 변수에 액세스할 수 있습니다. Variables를 참조하십시오. 16)

query HeroComparison($first: Int = 3) {
  leftComparison: hero(episode: EMPIRE) {
    ...comparisonFields
  }
  rightComparison: hero(episode: JEDI) {
    ...comparisonFields
  }
}
 
fragment comparisonFields on Character {
  name
  friendsConnection(first: $first) {
    totalCount
    edges {
      node {
        name
      }
    }
  }
}

{
  "data": {
    "leftComparison": {
      "name": "Luke Skywalker",
      "friendsConnection": {
        "totalCount": 4,
        "edges": [
          {
            "node": {
              "name": "Han Solo"
            }
          },
          {
            "node": {
              "name": "Leia Organa"
            }
          },
          {
            "node": {
              "name": "C-3PO"
            }
          }
        ]
      }
    },
    "rightComparison": {
      "name": "R2-D2",
      "friendsConnection": {
        "totalCount": 3,
        "edges": [
          {
            "node": {
              "name": "Luke Skywalker"
            }
          },
          {
            "node": {
              "name": "Han Solo"
            }
          },
          {
            "node": {
              "name": "Leia Organa"
            }
          }
        ]
      }
    }
  }
}

지금까지 쿼리 키워드와 쿼리 이름을 모두 생략하는 축약형 구문을 사용했지만 프로덕션 앱에서는 코드를 덜 모호하게 만드는 데 사용하는 것이 유용합니다.17)

다음은 작업 유형으로 키워드 query를 포함하고 작업 이름으로 HeroNameAndFriends를 포함하는 예입니다.18)

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

작업 유형은 쿼리, 변형 또는 구독이며 수행하려는 작업 유형을 설명합니다. 쿼리 약식 구문을 사용하지 않는 한 작업 유형이 필요합니다. 이 경우 작업에 대한 이름이나 변수 정의를 제공할 수 없습니다.19)

작업 이름은 작업에 대한 의미 있고 명시적인 이름입니다. 다중 작업 문서에서만 필요하지만 디버깅 및 서버 측 로깅에 매우 유용하므로 사용을 권장합니다. 문제가 발생하면(네트워크 로그 또는 GraphQL 서버 로그에 오류가 표시됨) 내용을 해독하는 대신 이름으로 코드베이스의 쿼리를 식별하는 것이 더 쉽습니다. 이것을 좋아하는 프로그래밍 언어의 함수 이름처럼 생각하십시오. 예를 들어 JavaScript에서는 익명의 함수로만 쉽게 작업할 수 있지만 함수에 이름을 지정하면 함수를 추적하고 코드를 디버그하고 호출될 때 기록하기가 더 쉽습니다. 마찬가지로 GraphQL 쿼리 및 변형 이름은 프래그먼트 이름과 함께 서버 측에서 다양한 GraphQL 요청을 식별하는 데 유용한 디버깅 도구가 될 수 있습니다.20)

지금까지 쿼리 문자열 안에 모든 인수를 작성했습니다. 그러나 대부분의 응용 프로그램에서 필드에 대한 인수는 동적입니다. 예를 들어 관심 있는 스타워즈 에피소드, 검색 필드 또는 필터 집합을 선택할 수 있는 드롭다운이 있을 수 있습니다.21)

이러한 동적 인수를 쿼리 문자열에 직접 전달하는 것은 좋은 생각이 아닙니다. 그러면 클라이언트 측 코드가 런타임에 쿼리 문자열을 동적으로 조작하고 GraphQL 관련 형식으로 직렬화해야 하기 때문입니다. 대신 GraphQL에는 쿼리에서 동적 값을 인수분해하고 별도의 사전으로 전달하는 일급 방법이 있습니다. 이러한 값을 변수라고 합니다. 22)

변수 작업을 시작할 때 세 가지 작업을 수행해야 합니다.23)

  1. 쿼리의 정적 값을 $variableName으로 바꿉니다.
  2. 쿼리에서 허용하는 변수 중 하나로 $variableName을 선언합니다.
  3. variableName: value 별도의 전송별(일반적으로 JSON) 변수 사전의 값 전달
    24)

query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

VARIABLES

{
  "episode": "JEDI"
}

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

이제 클라이언트 코드에서 완전히 새로운 쿼리를 생성할 필요 없이 단순히 다른 변수를 전달할 수 있습니다. 이것은 또한 일반적으로 쿼리의 어떤 인수가 동적일 것으로 예상되는지를 나타내는 좋은 방법입니다. 사용자가 제공한 값에서 쿼리를 구성하기 위해 문자열 보간을 수행해서는 안 됩니다.25)

변수 정의는 위 쿼리에서 ($episode: Episode)처럼 보이는 부분입니다. 유형이 지정된 언어의 함수에 대한 인수 정의처럼 작동합니다. 여기에는 $가 접두사로 붙은 모든 변수가 나열되고, 그 뒤에 해당 유형(이 경우에는 Episode)이 표시됩니다.26)

선언된 모든 변수는 스칼라, 열거형 또는 입력 개체 유형이어야 합니다. 따라서 복잡한 개체를 필드에 전달하려면 서버에서 일치하는 입력 유형을 알아야 합니다. 스키마 페이지에서 입력 개체 유형에 대해 자세히 알아보세요.27)

변수 정의는 선택 사항이거나 필수 사항일 수 있습니다. 위의 경우에는 !가 에피소드 유형 옆에 없으므로 선택 사항입니다. 그러나 변수를 전달하는 필드에 null이 아닌 인수가 필요한 경우에는 변수가 필수 입니다.28)

이러한 변수 정의의 구문에 대해 자세히 알아보려면 GraphQL 스키마 언어를 배우는 것이 좋습니다. 스키마 언어는 스키마 페이지에 자세히 설명되어 있습니다.29)

유형 선언 뒤에 기본값을 추가하여 쿼리의 변수에 기본값을 할당할 수도 있습니다.30)

query HeroNameAndFriends($episode: Episode = JEDI) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

모든 변수에 대해 기본값이 제공되면 변수를 전달하지 않고 쿼리를 호출할 수 있습니다. 변수 사전의 일부로 변수가 전달되면 기본값을 무시합니다.31)

위에서 변수를 사용하여 동적 쿼리를 구성하기 위해 수동 문자열 보간을 수행하지 않도록 하는 방법에 대해 논의했습니다. 인수에 변수를 전달하면 이러한 문제의 꽤 큰 부류가 해결되지만 변수를 사용하여 쿼리의 구조와 모양을 동적으로 변경하는 방법도 필요할 수 있습니다. 예를 들어 요약 및 상세 보기가 있는 UI 구성요소를 상상할 수 있습니다. 여기서 하나는 다른 것보다 더 많은 필드를 포함합니다.32)

query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}

VARIABLES
{
  "episode": "JEDI",
  "withFriends": false
}

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

위의 변수를 편집하여 대신 withFriends에 대해 true를 전달하고 결과가 어떻게 변경되는지 보십시오.33)

지시문이라고 하는 GraphQL의 새로운 기능을 사용해야 했습니다. 지시문은 필드 또는 조각 포함에 첨부될 수 있으며 서버가 원하는 방식으로 쿼리 실행에 영향을 줄 수 있습니다. 핵심 GraphQL 사양에는 사양을 준수하는 GraphQL 서버 구현에서 지원해야 하는 정확히 두 개의 지시문이 포함됩니다.34)

  • @include(if: Boolean) 인수가 true인 경우에만 결과에 이 필드를 포함합니다.35)
  • @skip(if: Boolean) 인수가 true이면 이 필드를 건너뜁니다.36)

지시문은 쿼리에서 필드를 추가 및 제거하기 위해 문자열 조작을 수행해야 하는 상황에서 벗어나는 데 유용할 수 있습니다. 서버 구현은 완전히 새로운 지시문을 정의하여 실험적 기능을 추가할 수도 있습니다.37)

GraphQL에 대한 대부분의 논의는 데이터 가져오기에 중점을 두고 있지만 완전한 데이터 플랫폼은 서버 측 데이터도 수정할 수 있는 방법이 필요합니다.38)

REST에서 모든 요청은 결국 서버에 일부 부작용을 일으킬 수 있지만 일반적으로 GET 요청을 사용하여 데이터를 수정하지 않는 것이 좋습니다. GraphQL도 유사합니다. 기술적으로 모든 쿼리를 구현하여 데이터 쓰기를 수행할 수 있습니다. 그러나 쓰기를 유발하는 모든 작업은 mutation을 통해 명시적으로 보내야 한다는 규칙을 설정하는 것이 유용합니다.39)

쿼리와 마찬가지로 변형 필드가 객체 유형을 반환하면 중첩 필드를 요청할 수 있습니다. 이것은 업데이트 후 개체의 새 상태를 가져오는 데 유용할 수 있습니다. 간단한 mutation 예를 살펴보겠습니다.40)

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

VARIABLES
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

createReview 필드가 새로 생성된 리뷰의 starscommentary 필드를 반환하는 방법에 유의하십시오. 이것은 한 번의 요청으로 필드의 새 값을 변경하고 쿼리할 수 있기 때문에, 예를 들어 필드를 증가시킬 때 기존 데이터를 변경할 때 특히 유용합니다.41)

또한 이 예에서 전달한 review 변수가 스칼라가 아님을 알 수 있습니다. 이것은 입력 객체 유형이며 인수로 전달할 수 있는 특별한 유형의 객체 유형입니다. 스키마 페이지에서 입력 유형에 대해 자세히 알아보세요.42)

mutation은 쿼리와 마찬가지로 여러 필드를 포함할 수 있습니다. 쿼리와 mutation 사이에는 이름 외에 한 가지 중요한 차이점이 있습니다. 43)

쿼리 필드가 병렬로 실행되지만 동안 mutation 필드는 차례로 차례로 실행됩니다.44)

즉, 한 요청에서 두 개의 mutation을 보내면 incrementCredits 첫 번째 mutation이 두 번째 시작 전에 완료되도록 보장되어 우리 자신과의 경쟁 조건으로 끝나지 않도록 합니다.45)

다른 많은 유형 시스템과 마찬가지로 GraphQL 스키마에는 인터페이스 및 공용체 유형을 정의하는 기능이 포함되어 있습니다. 스키마 가이드에서 이에 대해 알아보세요.46)

인터페이스 또는 공용체 유형을 반환하는 필드를 쿼리하는 경우 인라인 조각 을 사용하여 기본 구체 유형의 데이터에 액세스해야 합니다. 예를 들어 보면 가장 쉽게 볼 수 있습니다.47)

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
    ... on Human {
      height
    }
  }
}

VARIABLES
{
  "ep": "JEDI"
}

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}

이 쿼리에서 hero 필드는 에피소드 인수에 따라 Human 또는 Droid가 될 수 있는 Character 유형을 반환합니다. 직접 선택에서는 name과 같이 Character 인터페이스에 존재하는 필드만 요청할 수 있습니다.48)

구체적인 유형에 대한 필드를 요청하려면 유형 조건이 있는 Inline Fragments을 사용해야 합니다. 첫 번째 조각은 Droid에서 ...로 레이블이 지정되어 있기 때문에 primaryFunction 필드는 Hero에서 반환된 Character가 Droid 유형인 경우에만 실행됩니다. Human 유형의 height 필드와 유사합니다.49)

명명된 프래그먼트에는 항상 유형이 첨부되어 있으므로 명명된 프래그먼트도 같은 방식으로 사용할 수 있습니다.50)

GraphQL 서비스에서 어떤 유형을 반환할지 모르는 상황이 있다는 점을 감안할 때 클라이언트에서 해당 데이터를 처리하는 방법을 결정할 방법이 필요합니다. GraphQL은 메타 필드 __typename을 요청하여 쿼리의 어느 지점에서든 해당 지점의 개체 유형 이름을 가져올 수 있습니다.51)

{
  search(text: "an") {
    __typename
    ... on Human {
      name
    }
    ... on Droid {
      name
    }
    ... on Starship {
      name
    }
  }
}

{
  "data": {
    "search": [
      {
        "__typename": "Human",
        "name": "Han Solo"
      },
      {
        "__typename": "Human",
        "name": "Leia Organa"
      },
      {
        "__typename": "Starship",
        "name": "TIE Advanced x1"
      }
    ]
  }
}

search 쿼리에서는 세 가지 옵션 중 하나가 될 수 있는 공용체 유형을 반환합니다. __typename 필드 없이 클라이언트와 다른 유형을 구별하는 것은 불가능합니다.52)

GraphQL 서비스는 몇 가지 메타 필드를 제공하며 나머지는 Introspection 시스템을 노출하는 데 사용됩니다.53)


1)
On this page, you'll learn in detail about how to query a GraphQL server.
2)
At its simplest, GraphQL is about asking for specific fields on objects. Let's start by looking at a very simple query and the result we get when we run it:
3)
You can see immediately that the query has exactly the same shape as the result. This is essential to GraphQL, because you always get back what you expect, and the server knows exactly what fields the client is asking for.
4)
The field name returns a String type, in this case the name of the main hero of Star Wars, “R2-D2”.
5)
Oh, one more thing - the query above is interactive. That means you can change it as you like and see the new result. Try adding an appearsIn field to the hero object in the query, and see the new result.
6)
In the previous example, we just asked for the name of our hero which returned a String, but fields can also refer to Objects. In that case, you can make a sub-selection of fields for that object. GraphQL queries can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several roundtrips as one would need in a classic REST architecture.
7)
Note that in this example, the friends field returns an array of items. GraphQL queries look the same for both single items or lists of items, however we know which one to expect based on what is indicated in the schema.
8)
If the only thing we could do was traverse objects and their fields, GraphQL would already be a very useful language for data fetching. But when you add the ability to pass arguments to fields, things get much more interesting.
9)
In a system like REST, you can only pass a single set of arguments - the query parameters and URL segments in your request. But in GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches. You can even pass arguments into scalar fields, to implement data transformations once on the server, instead of on every client separately.
10)
Arguments can be of many different types. In the above example, we have used an Enumeration type, which represents one of a finite set of options (in this case, units of length, either METER or FOOT). GraphQL comes with a default set of types, but a GraphQL server can also declare its own custom types, as long as they can be serialized into your transport format.
11)
If you have a sharp eye, you may have noticed that, since the result object fields match the name of the field in the query but don't include arguments, you can't directly query for the same field with different arguments. That's why you need aliases - they let you rename the result of a field to anything you want.
12)
In the above example, the two hero fields would have conflicted, but since we can alias them to different names, we can get both results in one request.
13)
Let's say we had a relatively complicated page in our app, which lets us look at two heroes side by side, along with their friends. You can imagine that such a query could quickly get complicated, because we would need to repeat the fields at least once - one for each side of the comparison.
14)
That's why GraphQL includes reusable units called fragments. Fragments let you construct sets of fields, and then include them in queries where you need to. Here's an example of how you could solve the above situation using fragments:
15)
You can see how the above query would be pretty repetitive if the fields were repeated. The concept of fragments is frequently used to split complicated application data requirements into smaller chunks, especially when you need to combine lots of UI components with different fragments into one initial data fetch.
16)
It is possible for fragments to access variables declared in the query or mutation. See variables.
17)
Up until now, we have been using a shorthand syntax where we omit both the query keyword and the query name, but in production apps it's useful to use these to make our code less ambiguous.
18)
Here’s an example that includes the keyword query as operation type and HeroNameAndFriends as operation name :
19)
The operation type is either query, mutation, or subscription and describes what type of operation you're intending to do. The operation type is required unless you're using the query shorthand syntax, in which case you can't supply a name or variable definitions for your operation.
20)
The operation name is a meaningful and explicit name for your operation. It is only required in multi-operation documents, but its use is encouraged because it is very helpful for debugging and server-side logging. When something goes wrong (you see errors either in your network logs, or in the logs of your GraphQL server) it is easier to identify a query in your codebase by name instead of trying to decipher the contents. Think of this just like a function name in your favorite programming language. For example, in JavaScript we can easily work only with anonymous functions, but when we give a function a name, it's easier to track it down, debug our code, and log when it's called. In the same way, GraphQL query and mutation names, along with fragment names, can be a useful debugging tool on the server side to identify different GraphQL requests.
21)
So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic: For example, there might be a dropdown that lets you select which Star Wars episode you are interested in, or a search field, or a set of filters.
22)
It wouldn't be a good idea to pass these dynamic arguments directly in the query string, because then our client-side code would need to dynamically manipulate the query string at runtime, and serialize it into a GraphQL-specific format. Instead, GraphQL has a first-class way to factor dynamic values out of the query, and pass them as a separate dictionary. These values are called variables.
23)
When we start working with variables, we need to do three things:
24)
1. Replace the static value in the query with $variableName
2. Declare $variableName as one of the variables accepted by the query
3. Pass variableName: value in the separate, transport-specific (usually JSON) variables dictionary
25)
Now, in our client code, we can simply pass a different variable rather than needing to construct an entirely new query. This is also in general a good practice for denoting which arguments in our query are expected to be dynamic - we should never be doing string interpolation to construct queries from user-supplied values.
26)
The variable definitions are the part that looks like ($episode: Episode) in the query above. It works just like the argument definitions for a function in a typed language. It lists all of the variables, prefixed by $, followed by their type, in this case Episode.
27)
All declared variables must be either scalars, enums, or input object types. So if you want to pass a complex object into a field, you need to know what input type that matches on the server. Learn more about input object types on the Schema page.
28)
Variable definitions can be optional or required. In the case above, since there isn't an ! next to the Episode type, it's optional. But if the field you are passing the variable into requires a non-null argument, then the variable has to be required as well.
29)
To learn more about the syntax for these variable definitions, it's useful to learn the GraphQL schema language. The schema language is explained in detail on the Schema page.
30)
Default values can also be assigned to the variables in the query by adding the default value after the type declaration.
31)
When default values are provided for all variables, you can call the query without passing any variables. If any variables are passed as part of the variables dictionary, they will override the defaults.
32)
We discussed above how variables enable us to avoid doing manual string interpolation to construct dynamic queries. Passing variables in arguments solves a pretty big class of these problems, but we might also need a way to dynamically change the structure and shape of our queries using variables. For example, we can imagine a UI component that has a summarized and detailed view, where one includes more fields than the other.
33)
Try editing the variables above to instead pass true for withFriends, and see how the result changes.
34)
We needed to use a new feature in GraphQL called a directive. A directive can be attached to a field or fragment inclusion, and can affect execution of the query in any way the server desires. The core GraphQL specification includes exactly two directives, which must be supported by any spec-compliant GraphQL server implementation:
35)
@include(if: Boolean) Only include this field in the result if the argument is true.
36)
@skip(if: Boolean) Skip this field if the argument is true.
37)
Directives can be useful to get out of situations where you otherwise would need to do string manipulation to add and remove fields in your query. Server implementations may also add experimental features by defining completely new directives.
38)
Most discussions of GraphQL focus on data fetching, but any complete data platform needs a way to modify server-side data as well.
39)
In REST, any request might end up causing some side-effects on the server, but by convention it's suggested that one doesn't use GET requests to modify data. GraphQL is similar - technically any query could be implemented to cause a data write. However, it's useful to establish a convention that any operations that cause writes should be sent explicitly via a mutation.
40)
Just like in queries, if the mutation field returns an object type, you can ask for nested fields. This can be useful for fetching the new state of an object after an update. Let's look at a simple example mutation:
41)
Note how createReview field returns the stars and commentary fields of the newly created review. This is especially useful when mutating existing data, for example, when incrementing a field, since we can mutate and query the new value of the field with one request.
42)
You might also notice that, in this example, the review variable we passed in is not a scalar. It's an input object type, a special kind of object type that can be passed in as an argument. Learn more about input types on the Schema page.
43)
A mutation can contain multiple fields, just like a query. There's one important distinction between queries and mutations, other than the name:
44)
While query fields are executed in parallel, mutation fields run in series, one after the other.
45)
This means that if we send two incrementCredits mutations in one request, the first is guaranteed to finish before the second begins, ensuring that we don't end up with a race condition with ourselves.
46)
Like many other type systems, GraphQL schemas include the ability to define interfaces and union types. Learn about them in the schema guide.
47)
If you are querying a field that returns an interface or a union type, you will need to use inline fragments to access data on the underlying concrete type. It's easiest to see with an example:
48)
In this query, the hero field returns the type Character, which might be either a Human or a Droid depending on the episode argument. In the direct selection, you can only ask for fields that exist on the Character interface, such as name.
49)
To ask for a field on the concrete type, you need to use an inline fragment with a type condition. Because the first fragment is labeled as … on Droid, the primaryFunction field will only be executed if the Character returned from hero is of the Droid type. Similarly for the height field for the Human type.
50)
Named fragments can also be used in the same way, since a named fragment always has a type attached.
51)
Given that there are some situations where you don't know what type you'll get back from the GraphQL service, you need some way to determine how to handle that data on the client. GraphQL allows you to request __typename, a meta field, at any point in a query to get the name of the object type at that point.
52)
In the above query, search returns a union type that can be one of three options. It would be impossible to tell apart the different types from the client without the __typename field.
53)
GraphQL services provide a few meta fields, the rest of which are used to expose the Introspection system.
  • open/queries-and-mutations.txt
  • 마지막으로 수정됨: 2022/08/31 06:15
  • 저자 127.0.0.1