GraphQL

GraphQL（グラフQL）はAPI向けに作られたクエリ言語およびランタイムである。 ベンダーフリーな GraphQL財団の支援を受け、開発が進められている。 ウェブAPIの開発に、RESTやその他のWebサービスと比較して、効率的で堅牢、かつ柔軟なアプローチを提供する。GraphQLでは、クライアントが必要なデータの構造を定義することができ、サーバーからは定義したのと同じ構造のデータが返される。したがって、必要以上に大きなデータが返されるのを防ぐことができクエリの効率が良い。また、柔軟性と豊かな表現が可能なクエリ言語は複雑さを加えるため、シンプルなAPIには適さない可能性もある。GraphQLは、型システム、クエリ言語、実行セマンティクス、静的な検証、型チェックから構成される。 GraphQLは、データの問い合わせ (query)、書き換え (mutation)、購読 (subscription) をサポートする。 GraphQLでは、クライアントがクエリ内容を記したdocumentを送信し、GraphQLサービスがクエリを実行し結果を返信する。documentはDSLであるGraphQL query languageを用いて記述される。以下の例は明日の天気を取得するクエリのdocumentとGraphQLサービスから返されたクエリ結果のJSONである。 // document { tomorrow { weather rainyPercent } } { "tomorrow": { "weather": "cloudy", "rainyPercent": 30 } } リソースをURLパスで表現するRESTful APIと異なり、GraphQLはリソースをdocumentで表現する。GraphQL WebAPIの場合、単一のAPIエンドポイントへこのdocumentをPOSTする（例:https://API.internal./graphqlエンドポイントへdocumentをBodyとしてPOSTする）ことでクエリが実行される（参考: アプリケーションプログラミングインタフェース#様式 ）。 Documentを受け取ったGraphQLサービスはDocument中のfield (例: tomorrow) をトップレベルから1つずつresolverにより値へ解決する。クライアントが受け取りたい値はすべてfieldとして過不足なくDocumentに記載されているので、resolver群により全てのfieldを解決すれば過不足ないデータが1回のクエリで得られる。GraphQLサービス処理系はこのfieldからなるグラフ構造を上からresolverを用いて解決する役割を果たす。resolverはサービス実装者により実装される。 構造が事前に決められたリソースへアクセスするRESTful APIと異なり、GraphQLではその場でdocumentから与えられた構造に対しresolver群を連続適用して解決する。ゆえにRESTful APIでは定められたリソースしか1度のアクセスでは取得できないが、GraphQLではschema-validな任意のリソースを1度のアクセスで得られる。例えばRESTful APIでは「ある人の友人リスト」APIがあっても「ある人の友人の友人リスト」を得るためには1度友人リストを得たのちにクライアントからもう一度APIを叩く（クライアントで自前の再帰を行う）必要がある。一方GraphQLでは「Persion.friends field」resolverさえあれば{personA {friends {friends {name} } } }DocumentをPOSTするだけでresolverが連続適用されて「友人の友人リスト」が1度に得られる。 このようにGraphQLはクライアントからクエリ構造を渡せる柔軟性を持ち、また1度のクエリで必要十分な値のみを得られる効率性を持つ。 GraphQLサービスが受け入れられる型を、IDLである"type system definition language" (schema definition languageとも) で書かれたSchemaで記述する。 GraphQLはあくまでAPI仕様であり、通信方式の実装とは独立している。例えばGraphQL WebAPIを実装する際、documentをURLクエリ文字列に載せることも可能である（例:GET https://API.internal./graphql?query={tomorrow{weather}}）。 GraphQLではargumentを用いたクエリの制御が可能である。GraphQLオブジェクトの各field（例: tomorrow）は0個以上のargumentsを持てる。GraphQLサービスではクエリをトップレベルのfieldからresolverによって解決していくが、argumentsはその時にresolverへ渡されresolverの振る舞いを制御する。次の例では"都道府県名/name" argumentと"天気/weather" argumentを用い、都道府県-街リストから東京/tokyoに含まれる晴れ/sunnyの街を取得する。 // document { prefecture(name: "Tokyo") { prefName cities(weather: "sunny") { cityName rainyPercent }}} { "prefecture": { { "prefName": "Tokyo", "cities": [ { "cityName": "Shinjuku", "rainyPercent": 10 },{ "cityName": "Ikebukuro", "rainyPercent": 0 }]}}} GraphQLではdirectivesを用いたクエリの制御が可能である。GraphQLオブジェクトの各field（例: tomorrow）はdirectiveを持てる。directivesは処理系に依存したあらゆる処理を指定するアノテーションである。全ての処理系に実装されるdirectiveの例としてfieldの無視を指定する@include(if: Boolean)がある。 { sinjuku { weather } ikebukuro @include(if: false) { weather rainyPercent }} { "sinjuku": { "weather": "cloudy" }} 全てのresolverに@include相当の引数を用意すれば同等の機能を提供できるが、directivesを用いることでresolverに手を加えずかつdirective処理系1箇所での実装のみでこの機能を提供できる。 directivesはGraphQLサービス処理系以外にも利用されうる。例えばAWS AmplifyはSchema上のdirectivesに基づいたクラウドリソースプロビジョニングコード生成（"GraphQL Transform"）を実装している。@modelが付与されたfieldはresolverが自動で生成されかつDynamoDBにリソースが準備される。これはGraphQLサービス処理系ではなく、Amplify CLIによっておこなわれる。 Documentは1つ以上のOperationDefinitionからなる。1つのみの場合Nameが省略可能であり、さらにOperationTypeがqueryならこれも省略できる。 // document OpType Name [VarDef][Directives] { ... } OpType Name [VarDef][Directives] { ... } // e.g. query ExampleQuery1 { resource } query ExampleQuery2 ($var: S) @skip(if: false) { time } FieldはNameからなっており、Alias, Arguments, Directivesを利用できる。さらにサブFieldを持つことができる。 // document { [Alias] Name [Arguments][Directives] [SelectionSet] } // e.g.1 - simple { resource } // e.g.2 - full { time: resource (arg1: "arg") @skip(if:false) { subResource } } 値解決（Value Resolution）はFieldの値を算出するステップである。ResolveFieldValue関数インターフェスに該当し、[オブジェクト型, オブジェクト値, field名, arguments]からfieldの値を生成する（例: Person型, Obamaオブジェクト, job fieldName, no argument => presidentfield value)。擬似JavaScriptコードで以下になる。 function ResolveFieldValue(objectType, objectValue, fieldName, argumentValues){ const resolver = getExternalResolver(ojectType, fieldName); return resolver(objectValue, argumentValues); } GraphQLは、2012年にFacebookの内部で開発され、2015年に公開された。2018年11月7日、GraphQLプロジェクトは、Facebookから非営利のLinux Foundationがホストする新たに設立されたGraphQL Foundationに移譲された。2012年以来、GraphQLの利用数は、GraphQLの作者のLee Byronが想定したとおりのスケジュールに正確に従って増加している 。Byronの目標は、GraphQLをWebプラットフォーム全体で利用されるようにすることである。 2018年2月9日、GraphQL Schema Definition Language（SDL）が仕様の一部となった。 GraphQLクライアントは適切なdocumentをGraphQL APIエンドポイントへPOSTするだけでクエリを実行できるため、ライブラリを採用せずとも容易にクエリを実行できる。 より高度な機能を有するGraphQLクライアント実装としては、Apollo ClientとRelayがある。 GraphQLサーバーは複数の言語で利用可能であり、Haskell、JavaScript、Perl、Python、Ruby、Java、C#、Scala、Go、Elixir、Erlang、PHP、R、Clojureで書かれたものがある。 GitHubは第4世代のAPIとしてGraphQL API「GitHub GraphQL API v4」を提供している。 GitHubはその選定理由として「more flexibility for our integrators（我々のインテグレータに対する更なる柔軟性）」を挙げている。GitHubではIssueをプロジェクト管理アプリに組み込むシステムインテグレータ、CI/CDパイプラインを管理するエンジニアやライブラリ作者、レポジトリ統計を取り出す個人など、多様なAPI利用者を抱えている。その多様な要求から、全ての利用者に必要かつ十分な（オーバーフェッチもアンダーフェッチもしない）APIを提供することは困難である（無数のREST APIエンドポイント・サーバー実装は管理しきれない）。結果として一部の利用者にとっては柔軟性が足りなく感じられていた。GitHubはレスポンス種に限りがあるREST APIから利用者側がレスポンス（クエリ構造）を指定するGraphQLにAPIを変更し、上記の問題を解決した。 Query by Example OpenAPI Specification（英語版）