GraphQL

概要

GraphQLはAPI向けに作られたクエリ言語(SQLと同じジャンル)、実行環境である。

従来のRESTでは事前に決められたリソースをURLを指定して取得するのに対し、GraphQLでは必要なものを指定できる。 複数のリソースを取得するとき、RESTではそれぞれAPIを呼び出す必要があるが、GraphQLでは1回のAPI呼び出しで完了する。 Reactなどと組み合わせて使用される。

基本用語

ミューテーション

データを変更する。 アプリケーションの状態を変更するために作成する。 ミューテーションはアプリケーションで使われる動詞と対応するのが望ましい。 サービスに対してできることがミューテーションとして定義される。

type Mutation {
  postPhoto(
    name: String!
    description: String
    category: PhotoCategory=PORTRAIT
  ): Photo!
}

schema {
  query: Query
  mutation: Mutation
}
mutation {
  postPhoto(name: "Sending the Palisades") {
    id
    url
    created
    postedBy {
      name
    }
  }
}

フラグメント

フラグメントは複数の場所で使いまわすことができる選択セット。

query {
  allTrails {
    ...trailStatus
    ...trailDetails
  }
}

クエリ変数

クエリ内の静的な値を置き換えて動的な値を渡せる。

サブスクリプション

サーバを更新するたびにクライアントで情報をリアルタイムに受け取る。

subscription {
  liftStatusChange {
    name
    capacity
    status
  }
}

{ “data”: { “liftStatusChange”: null } }

mutation closeLift {
  setLiftStatus(id: "astra-express" status: HOLD) {
    name
    status
  }
}

{ “data”: { “setLiftStatus”: { “name”: “Astra Express”, “status”: “HOLD” } } }

変更内容がサブスクリプションしているクライアントに向けてプッシュされる。

イントロスペクション

APIスキーマの詳細を取得できる機能。

query {
  __schema {
    types {
      name
      description
    }
  }
}

{ “data”: { “__schema”: { “types”: [ { “name”: “Lift”, “description”: “A `Lift` is a chairlift, gondola, tram, funicular, pulley, rope tow, or other means of ascending a mountain.” }, { “name”: “ID”, “description”: “The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\”4\“`) or integer (such as `4`) input value will be accepted as an ID.” }, { “name”: “String”, “description”: “The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.” }, { “name”: “Int”, “description”: “The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.” }, { “name”: “Boolean”, “description”: “The `Boolean` scalar type represents `true` or `false`.” }, { “name”: “Trail”, “description”: “A `Trail` is a run at a ski resort” }, { “name”: “LiftStatus”, “description”: “An enum describing the options for `LiftStatus`: `OPEN`, `CLOSED`, `HOLD`” }, { “name”: “TrailStatus”, “description”: “An enum describing the options for `TrailStatus`: `OPEN`, `CLOSED`” }, { “name”: “SearchResult”, “description”: “This union type returns one of two types: a `Lift` or a `Trail`. When we search for a letter, we’ll return a list of either `Lift` or `Trail` objects.” }, { “name”: “Query”, “description”: null }, { “name”: “Mutation”, “description”: null }, { “name”: “Subscription”, “description”: null }, { “name”: “CacheControlScope”, “description”: null }, { “name”: “Upload”, “description”: “The `Upload` scalar type represents a file upload.” }, { “name”: “__Schema”, “description”: “A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.” }, { “name”: “__Type”, “description”: “The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByUrl`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.” }, { “name”: “__TypeKind”, “description”: “An enum describing what kind of type a given `__Type` is.” }, { “name”: “__Field”, “description”: “Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.” }, { “name”: “__InputValue”, “description”: “Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.” }, { “name”: “__EnumValue”, “description”: “One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.” }, { “name”: “__Directive”, “description”: “A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL’s execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.” }, { “name”: “__DirectiveLocation”, “description”: “A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.” } ] } } }

query liftDetails {
  __type(name: "Lift") {
    name
    fields {
      name
      description
      type {
        name
      }
    }
  }
}

{ “data”: { “__type”: { “name”: “Lift”, “fields”: [ { “name”: “id”, “description”: “The unique identifier for a `Lift` (id: \”panorama\“)”, “type”: { “name”: null } }, { “name”: “name”, “description”: “The name of a `Lift`”, “type”: { “name”: null } }, { “name”: “status”, “description”: “The current status for a `Lift`: `OPEN`, `CLOSED`, `HOLD`”, “type”: { “name”: “LiftStatus” } }, { “name”: “capacity”, “description”: “The number of people that a `Lift` can hold”, “type”: { “name”: null } }, { “name”: “night”, “description”: “A boolean describing whether a `Lift` is open for night skiing”, “type”: { “name”: null } }, { “name”: “elevationGain”, “description”: “The number of feet in elevation that a `Lift` ascends”, “type”: { “name”: null } }, { “name”: “trailAccess”, “description”: “A list of trails that this `Lift` serves”, “type”: { “name”: null } } ] } } }

スキーマ

GraphQLによって、APIは型の集合としてとらえられるようになる。 データ型の集合をスキーマという。

ユニオン型・インターフェース型

異なる型が混在するフィールドを表現するのに使う。 含まれている複数の型がまったく異なるものであればユニオン型を、共通のフィールドがある場合はインターフェースを利用するのが一般的。

ドキュメント

"""
最低一度は認可されたユーザ
"""
type User {

  """
  ユーザの一意のGitHubログインID
  """
  githubLogin: ID!

  """
  ユーザの姓名
  """
  name: String

  """
  このユーザが投稿した全写真
  """
  postedPhotos: [Photo!]!

  """
  このユーザが含まれる全写真
  """
  inPhotos: [Photo!]!
}

type Mutation {
  """
  GitHubユーザで認可
  """
  githubAuth(
    "ユーザの認可のために送信されるGitHubの一意のコード"
    code: String!
  ): AuthPayload!
}

Emacs設定

Emacsでコード、クエリアクセス、結果出力をプレーンテキストで残す設定。

graphql-modeを追加する。 davazp/graphql-mode: An Emacs mode for GraphQL

ob-graphqlを追加する。 jdormit/ob-graphql: GraphQL execution backend for org-babel

取得元を入力#+BEGIN_SRCに入れることで、org-babelでクエリを実行できる。

query GetContinents {
    continent(code: "AF") {
        name
    code
    }
}

{ “data”: { “continent”: { “name”: “Africa”, “code”: “AF” } } }

Tasks

Reference

GraphQL - Guides Index

ドキュメント。

Explorer - GitHub Docs

GitHubのGraphQL API。

A Gentle Introduction To Graph Theory - DEV Community

ノードとエッジについてのブログ記事。

SWAPI GraphQL API

サンプルでどんな感じかテストできるサイト。 スターウォーズの情報にアクセスできる。

query {
  person(personID: 5){
    name
    birthYear
    created
    filmConnection {
      films {
        title
      }
    }
  }
}

null

Archives

DONE 初めてのGraphQL ―Webサービスを作って学ぶ新世代API

  • 86, 100, 124, 147, 185, 190, 197, 229

実際にコードとして書いていくのにはあまり評判がよくない。概要を知るためには役立つよう。 コードサンプルはJavaScript, node.js, expressなので、元からこれらについて知らないとあまりピンとこない感じがする。

メモ。 グラフ理論。グラフは概念同士の関係性を図示するための優れた考え方。 SQLはデータベースのための問い合わせ言語で、GraphQLはインターネットのための問い合わせ言語。