GraphQLの詳細:何、どのように、なぜ

現在、GraphQLは誇張することなく、ITモードの最新トレンドです。 そして、それがどんな種類のテクノロジーであるか、それをどのように使用するか、そしてなぜそれがあなたにとって有用であるかもしれないかをまだ知らないなら、今日私たちが公開している記事はあなたのために書かれています。 ここでは、ポップコーン企業のAPIのデータスキームの例を使用して、GraphQLの基本について説明します。 特に、データ型、クエリ、突然変異について話しましょう。







GraphQLとは何ですか?



GraphQLは、クライアントアプリケーションがデータを処理するために使用するクエリ言語です。 GraphQLは「スキーマ」などの概念に関連付けられています。これにより、アプリケーション内のデータの作成、読み取り、更新、削除を整理できます(つまり、頭字語CRUDで参照されるデータウェアハウスを操作するときに使用される4つの基本機能があります) -作成、読み取り、更新、削除)。



GraphQLは、「データベース内」ではなく「アプリケーション」内のデータを操作するために使用されると上記で述べました。 実際のところ、GraphQLはデータソースに依存しないシステムです。つまり、作業を整理するためにどこで整理するかは関係ありません。



GraphQLについて何も知らずにこの技術の名前を見ると、非常に複雑で混乱しているものに直面しているように見えるかもしれません。 技術の名前には「グラフ」という言葉があります。 これは、それをマスターするために、グラフデータベースを操作することを学ぶ必要があるということですか? また、名前に「QL」(「クエリ言語」、つまり「クエリ言語」を意味する場合があります)が含まれているという事実は、GraphQLを使用したい人はまったく新しいプログラミング言語を習得しなければならないという意味ですか?



これらの恐怖は完全に正当化されていません。 あなたを安心させるために-これはこの技術についての残酷な真実です。それは単にGET



またはPOST



リクエストを装飾しただけです。 一般に、GraphQLはデータ編成とその相互作用に関連するいくつかの新しい概念を導入しますが、このテクノロジーの内部メカニズムは古き良きHTTPリクエストに依存しています。



RESTテクノロジーの再考



柔軟性は、GraphQLテクノロジーを有名なRESTテクノロジーと区別するものです。 RESTを使用する場合、すべてが正しく実行されると、通常、特定のリソースまたはアプリケーションデータタイプの特性を考慮してエンドポイントが作成されます。



たとえば、エンドポイント/api/v1/flavors



に対してGET



リクエストを実行する場合、次のような応答を送信することが期待されます。



 [ {  "id": 1,   "name": "The Lazy Person's Movie Theater",   "description": "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" }, {   "id": 2,   "name": "What's Wrong With You Caramel",   "description": "You're a crazy person that likes sweet popcorn. Congratulations." }, {   "id": 3,   "name": "Gnarly Chili Lime",   "description": "The kind of popcorn you make when you need a good smack in the face."} ]
      
      





この答えに壊滅的な問題はありませんが、ユーザーインターフェイスについて、またはこのデータをどのように使用するかについて考えてみましょう。



使用可能なタイプのポップコーンの名前のみを含む(そして何も含まない)シンプルなリストをインターフェイスに表示する場合、このリストは次のようになります。









ポップコーンの種類のリスト



ここで私たちは困難な状況にあることがわかります。 description



フィールドを使用しないことを決定することもできますが、このフィールドをクライアントに送信しなかったように座ってふりをするつもりですか? 他に何ができますか? そして、数か月後に、ユーザーにとってアプリケーションがなぜそんなに遅いのかと聞かれたら、このアプリケーションを作成した会社の経営陣と会わないようにする必要があります。



実際、サーバーがクライアントのリクエストに応じて不要なデータを送信するという事実は、完全に私たちのせいではありません。 RESTは、ウェイターが訪問者に尋ねるレストランと比較できるデータ取得メカニズムです:「あなたは何が欲しいですか?」そして、彼の願いに特に注意を払わずに、彼は彼に言います:「私たちが持っているものを持ってきます」 。



ジョークを無視すると、実際のアプリケーションでは問題が発生する可能性があります。 たとえば、価格情報、メーカーに関する情報、栄養情報など、ポップコーンの各タイプに関するさまざまな追加情報を表示できます(「ビーガンポップコーン!」)。 同時に、柔軟性のないRESTエンドポイントにより、特定の種類のポップコーンに関する特定のデータを取得することが非常に難しくなります。これにより、システムに不当に高い負荷がかかり、結果として得られるソリューションは開発者が誇ることができるソリューションからはほど遠いという事実につながります。



GraphQLテクノロジーがどのようにRESTテクノロジーを使用したかを改善する方法



上記の状況の表面的な分析は、私たちが小さな問題に過ぎないように思えるかもしれません。 「クライアントに不要なデータを送信することの何が問題になっていますか?」 「不要なデータ」が大きな問題になりうる範囲を理解するために、GraphQLはFacebookによって開発されたことを思い出してください。 この会社は、1秒あたり何百万ものリクエストに対応する必要があります。



これはどういう意味ですか? そして、そのようなボリュームではすべての詳細が重要であるという事実。



GraphQLは、レストランとの類推を続けると、訪問者に「何を」運ぶのではなく、訪問者が注文したものを正確にもたらします。



GraphQLから、データが使用されるコンテキストに焦点を当てた応答を取得できます。 この場合、「ワンタイム」アクセスポイントをシステムに追加したり、多くの要求を実行したり、複数階の条件構造を作成したりする必要はありません。



GraphQLはどのように機能しますか?



すでに述べたように、GraphQLは単純なGET



またはPOST



リクエストに依存して、クライアントにデータを送信し、クライアントからデータを受信します。 この考えをより詳細に検討すると、GraphQLには2種類のクエリがあることがわかります。 最初のタイプには、データの読み取り要求が含まれます。これは、GraphQLの用語では単にクエリと呼ばれ、頭字語CRUDの文字R(読み取り、読み取り)を指します。 2番目のタイプのクエリは、データを変更するためのクエリであり、GraphQLでは変更と呼ばれます。 これらは、頭字語CRUDのアクスルボックスC、U、およびDに関連しています。つまり、レコードを作成、作成、更新、および削除するために使用します。



これらのすべてのクエリと変更は、 GET



またはPOST



リクエストの形式で、たとえばhttps://myapp.com/graphql



ように見えるGraphQLサーバーのURLに送信されます。 これについては、以下で詳しく説明します。



GraphQLクエリ



GraphQLクエリは、特定のデータを受信するためのサーバーへの要求を表すエンティティです。 たとえば、データを入力する特定のユーザーインターフェイスがあります。 このデータについては、サーバーに戻り、リクエストを実行します。 従来のREST APIを使用する場合、リクエストはGETリクエストの形式を取ります。 GraphQLを使用する場合、新しいクエリ構文が使用されます。



 { flavors {   name } }
      
      





そのJSONですか? またはJavaScriptオブジェクトですか? どちらもありません。 すでに述べたように、GraphQLテクノロジーの名前では、最後の2文字QLは「クエリ言語」、つまりクエリ言語を意味します。 文字通り、データ要求を記述するための新しい言語です。 これはすべて、かなり複雑なものの説明のように聞こえますが、実際には複雑なことは何もありません。 上記のクエリを分析してみましょう。



 { //    ,   . }
      
      





すべての要求は「ルート要求」で始まり、要求の実行中に取得する必要があるものはフィールドと呼ばれます。 混乱を避けるために、これらのエンティティを「スキーマのクエリフィールド」と呼ぶのが最適です。 そのような名前があなたに理解できないと思われる場合-少し待ってください-以下でスキームについて詳しく説明します。 ここで、ルートクエリで、 flavors



フィールドを要求しflavors



。



 { flavors {   //  ,        flavor. } }
      
      





特定のフィールドをリクエストする場合、リクエストに応答するオブジェクトごとに受信する必要があるネストされたフィールドも指定する必要があります(リクエストに応答するオブジェクトは1つだけであると予想される場合でも)。



 { flavors {   name } }
      
      





結果はどうなりますか? このようなリクエストをGraphQLサーバーに送信すると、次のような適切な形式の適切な回答が得られます。



 { "data": {   "flavors": [     { "name": "The Lazy Person's Movie Theater" },     { "name": "What's Wrong With You Caramel" },     { "name": "Gnarly Chili Lime" }   ] } }
      
      





余分なものはないことに注意してください。 より明確にするために、アプリケーションの別のページでデータを取得するために実行される別のリクエストを次に示します。



 { flavors {   id   name   description } }
      
      





このリクエストに応えて、次のものを取得します。



 { "data": {   "flavors": [     { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" },     { "id": 2, "name": "What's Wrong With You Caramel", description: "You're a crazy person that likes sweet popcorn. Congratulations." },     { "id": 3, "name": "Gnarly Chili Lime", description: "A friend told me this would taste good. It didn't. It burned my kernels. I haven't had the heart to tell him." }   ] } }
      
      





ご覧のとおり、GraphQLは非常に強力なテクノロジーです。 同じエンドポイントを使用し、リクエストへの回答は、これらのリクエストが実行されるページを埋めるために必要なものに正確に対応しています。



flavor



オブジェクトを1つだけ取得する必要がある場合は、GraphQLが引数を処理できるという事実を利用できます。



 { flavors(id: "1") {   id   name   description } }
      
      





ここでは、コード内のオブジェクトの特定の識別子( id



)、必要な情報を厳密に設定しますが、そのような場合、動的な識別子も使用できます。



 query getFlavor($id: ID) { flavors(id: $id) {   id   name   description } }
      
      





ここで、最初の行では、リクエストに名前を付け(名前は任意に選択され、 getFlavor



はpizza



ようなものに置き換えることができ、リクエストは動作し続けます)、リクエストが期待する変数を宣言します。 この場合、スカラー型ID



の識別子( id



)がリクエストに渡されることが想定されています(以下で型について説明します)。



リクエストの実行時に静的id



または動的id



どちらid



使用されるかに関係なく、同様のリクエストに対する応答は次のようになります。



 { "data": {   "flavors": [     { "id": 1, "name": "The Lazy Person's Movie Theater", description: "That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!" }   ] } }
      
      





ご覧のとおり、すべてが非常に便利に配置されています。 おそらく、あなた自身のプロジェクトでGraphQLを使用することを考え始めているでしょう。 そして、すでに説明したことはすばらしいように見えますが、GraphQLの美しさは、ネストされたフィールドで機能する場所であります。 このスキームでは、さまざまな種類のポップコーンの栄養価に関する情報を含むnutrition



と呼ばれる別のフィールドがあると仮定します。



 { flavors {   id   name   nutrition {     calories     fat     sodium   } } }
      
      





データウェアハウスでは、各flavor



オブジェクトにネストされたnutrition



オブジェクトが含まれているように見えるかもしれません。 しかし、これは完全に真実ではありません。 GraphQLを使用すると、独立しているが接続されているデータソースへの呼び出しを単一のクエリに結合できます。これにより、データベースを非正規化する必要なく、埋め込みデータを操作する便利な回答を受け取ることができます。



 { "data": {   "flavors": [     {       "id": 1,       "name": "The Lazy Person's Movie Theater",       "nutrition": {         "calories": 500,         "fat": 12,         "sodium": 1000       }     },     ...   ] } }
      
      





これにより、プログラマの生産性とシステムの速度が大幅に向上します。



これまで、読み取り要求について説明してきました。 データ更新リクエストはどうですか? それらを使用しても同じ利便性が得られますか?



GraphQLの突然変異



GraphQLクエリはデータをロードしますが、データに変更を加えるのは突然変異です。 突然変異は、ユーザーデータをサードパーティAPIに送信するなど、さまざまなタスクを解決するための基本的なRPC(リモートプロシージャコール)メカニズムの形で使用できます。



突然変異を記述するとき、クエリを生成するときに使用した構文に似た構文が使用されます。



 mutation updateFlavor($id: ID!, $name: String, $description: String) { updateFlavor(id: $id, name: $name, description: $description) {   id   name   description } }
      
      





ここでは、 updateFlavor



ミューupdateFlavor



を宣言し、いくつかの変数updateFlavor



、 name



およびdescription



ます。 クエリの記述に使用されるのと同じスキームに従って動作し、 mutation



キーワードを使用して変数フィールド(ルート突然変異)を「作成」し、その後に突然変異を説明する名前と、対応するデータ変更要求を形成するために必要な変数のセットを続けます。



これらの変数には、変更しようとしているもの、または発生させたい突然変異が含まれます。 また、突然変異の後、いくつかのフィールドの返却をリクエストできることに注意してください。



この場合、レコードを変更した後、フィールドid



、 name



、およびdescription



を取得する必要がありdescription



。 これは、楽観的なインターフェイスのようなものを開発する際に役立ち、変更されたデータを変更後に受信する要求を満たす必要がなくなります。



スキーマを設計し、GraphQLサーバーに接続する



これまで、GraphQLがクライアント上でどのように機能するか、およびクエリを実行する方法について説明してきました。 次に、これらのリクエストに応答する方法について説明します。



▍GraphQLサーバー



GraphQLクエリを実行するには、そのようなクエリを送信できるGraphQLサーバーが必要です。 GraphQLサーバーは、通常のHTTPサーバーです(JavaScriptで記述する場合、ExpressまたはHapiを使用して作成されたサーバーにすることができます)。GraphQLダイアグラムが添付されます。



 import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true })) app.listen(4000)
      
      





スキームに「参加」することにより、クライアントから受け取ったリクエストをスキームに渡し、そのスキームに回答を返すメカニズムを意味します。 それは空気が部屋に入る空気フィルターのようなものです。



「フィルタリング」のプロセスは、クライアントからサーバーに送信された要求または変更に関連付けられています。 クエリと突然変異の両方は、ルートクエリまたはスキーマのルート突然変異で定義されたフィールドに関連する関数を使用して解決されます。



上記は、Express JavaScriptライブラリを使用して作成されたHTTPサーバーフレームワークの例です。 Facebookのexpress-graphql



のgraphqlHTTP



関数を使用して、スキームを「アタッチ」し(別のファイルに記述されていると想定)、ポート4000でサーバーを実行します。つまり、クライアントは、このサーバーのローカル使用について、アドレスhttp://localhost:4000/graphql



。



▍データ型とリゾルバ



GraphQLサーバーの動作を保証するには、スキーマを準備し、スキーマにアタッチする必要があります。



上記のルートクエリまたはルートミューテーションでのフィールドの宣言について説明したことを思い出してください。



 import gql from 'graphql-tag' import mongodb from '/path/to/mongodb' //  -  . ,  `mongodb`     MongoDB. const schema = { typeDefs: gql`   type Nutrition {     flavorId: ID     calories: Int     fat: Int     sodium: Int   }   type Flavor {     id: ID     name: String     description: String     nutrition: Nutrition   }   type Query {     flavors(id: ID): [Flavor]   }   type Mutation {     updateFlavor(id: ID!, name: String, description: String): Flavor   } `, resolvers: {   Query: {     flavors: (parent, args) => {       // ,  args  ,  { id: '1' }       return mongodb.collection('flavors').find(args).toArray()     },   },   Mutation: {     updateFlavor: (parent, args) => {       // ,  args    { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' }       //  .       mongodb.collection('flavors').update(args)       //  flavor  .       return mongodb.collection('flavors').findOne(args.id)     },   },   Flavor: {     nutrition: (parent) => {       return mongodb.collection('nutrition').findOne({         flavorId: parent.id,       })     }   }, }, } export default schema
      
      





GraphQLスキーマのフィールドの定義は、型宣言( typeDefs



)とresolver



の2つの部分で構成されていresolver



。 typeDefs



は、アプリケーションで使用されるデータの型宣言が含まれています。 たとえば、先ほど、サーバーからflavor



オブジェクトのリストを取得するリクエストについて説明しました。 サーバーに同様のリクエストを行うには、次の3つの手順を実行する必要があります。



  1. flavor



    オブジェクトデータがどのように見えるかを回路図に伝えます(上記の例では、 type Flavor



    広告のように見えます)。
  2. type Query



    ルートフィールドでフィールドを宣言します(これはtype Query



    値のflavors



    プロパティです)。
  3. type Query



    ルートフィールドで宣言されたフィールドに従って記述されたresolvers.Query



    オブジェクト認識機能を宣言しresolvers.Query



    。


次に、 typeDefs



注目しましょう。 ここでは、データの形状に関するスキーマ情報を提供します。 つまり、GraphQLに、対応するタイプのエンティティに含まれる可能性のあるさまざまなプロパティについて伝えます。



 type Flavor { id: ID name: String description: String nutrition: Nutrition }
      
      





type Flavor



宣言は、 flavor



オブジェクトに、タイプID



id



フィールド、 String



タイプのname



フィールド、 String



タイプのdescription



フィールドString



およびnutrition



タイプのNutrition



フィールドを含めることができることを示します。



nutrition



の場合、ここではtypeDefs



宣言された別の型の名前を使用します。 ここでは、 type Nutrition



コンストラクトは、ポップコーンの栄養データ形式を記述しています。



ここでは、この資料の冒頭にあるように、「データベース」ではなく「アプリケーション」について話していることに注意してください。 上記の例では、データベースがあると想定していますが、アプリケーションのデータはどのソースからでも取得できます。 サードパーティのAPIまたは静的ファイルである可能性もあります。



type Flavor



宣言で行ったように、ここでは、 nutrition



オブジェクトに含まれるフィールドの名前を指定します。これらのフィールド(プロパティ)のデータ型として、GraphQLでスカラーデータ型と呼ばれるものを使用します。 この記事の執筆時点で、GraphQLは5つの組み込みスカラーデータ型をサポートしていました。





これらのスカラー型に加えて、自分で定義した型にプロパティを割り当てることもできます。 これは、 type Flavor



コンストラクトで説明されているnutrition



特性をNutrition



割り当てることで行いました。



 type Query { flavors(id: ID): [Flavor] }
      
      





type Query



のルートタイプ(前に説明した「ルートクエリ」)を記述するtype Query



構造では、要求できるフィールドの名前を宣言します。 さらに、このフィールドを宣言することにより、返されるデータ型とともに、リクエストに含まれる引数を指定します。



この例では、スカラー型ID



id



引数を受け取る可能性がありID



。 このような要求に対する回答として、デバイスがFlavor



タイプのデバイスに似ているオブジェクトの配列が期待されます。



queryクエリレコグナイザーの接続



ルートtype Query



にフィールドtype Query



定義ができたので、リゾルバー関数と呼ばれるものを記述する必要があります。



これは、GraphQLが多かれ少なかれ「停止」する場所です。resolvers



スキーマオブジェクトを見て、そのQuery



中にネストされたオブジェクトを見るflavors



と、関数が割り当てられているプロパティを見ることができます。この関数は、フィールドのリゾルバと呼ばflavors



れ、ルートで宣言されtype Query



ます。



 typeDefs: gql`…`, resolvers: { Query: {   flavors: (parent, args) => {     // ,  args    { id: '1' }     return mongodb.collection('flavors').find(args).toArray()   }, }, … },
      
      





- . parent



— , , args



, . context



, . «» ( — , ).



, , . GraphQL « » . , , .



GraphQL , , . JSON-, JSON-, ( GraphQL ).



- flavors



MongoDB, args



( ) .find()



, , .



▍



-, GraphQL, , , , nutrition



. , , Nutrition



, , , , flavor



. , / .



GraphQL , type Flavor



nutrition



type Nutrition



, . , , flavor



.



 typeDefs: gql`   type Nutrition {     flavorId: ID     calories: Int     fat: Int     sodium: Int   }   type Flavor {     […]     nutrition: Nutrition   }   type Query {…}   type Mutation {…} `, resolvers: {   Query: {     flavors: (parent, args) => {…},   },   Mutation: {…},   Flavor: {     nutrition: (parent) => {       return mongodb.collection('nutrition').findOne({         flavorId: parent.id,       })     }   }, },
      
      





resolvers



, , Query



, Mutation



Flavor



. , typeDefs



.



Flavors



, , nutrition



-. , Flavor



. , : « , nutrition



, type Flavor



».



MongoDB, , parent



, -. , parent



, , , flavors



. , flavor



, :



 { flavors {   id   name   nutrition {     calories   } } }
      
      





flavor



, flavors



, nutrition



, parent



. , , , MongoDB, parent.id



, id



flavor



, .



parent.id



, nutrition



flavorId



, flavor



.



▍



, , . , . type Mutation



, , updateFlavor



, , .



 type Mutation { updateFlavor(id: ID!, name: String, description: String): Flavor }
      
      





: « , updateFlavor



id



ID



( , !



, GraphQL , ), name



String



description



String



». , , Flavor



( — , id



, name



, description



, , , nutrition



).



 { typeDefs: gql`…`, resolvers: {   Mutation: {     updateFlavor: (parent, args) => {       // ,  args    { id: '1', name: 'Movie Theater Clone', description: 'Bring the movie theater taste home!' }       //  .       mongodb.collection('flavors').update(         { id: args.id },         {           $set: {             ...args,           },         },       )       //  flavor  .       return mongodb.collection('flavors').findOne(args.id)     },   }, }, }
      
      





- updateFlavor



, : , , — , flavor



.



, , flavor



. これはなぜですか?



, , . , flavor



, .



args



? はい、できます。 , , , 100% , . , , , , , .



GraphQL?



, , , , , GraphQL-API.



, GraphQL , . , . , . , , , GraphQL REST . , , , GraphQL.



▍ ,



, HTTP-, , , , — . GraphQL , , , , ( ).



, , ( — ), GraphQL .



▍ , ,



, , « ». , , , . . GraphQL .



▍ ,



REST API, : , . , -, iOS Android, API . , , , , « » .



, , , HTTP, API (, , ).



▍ GraphQL — ? REST API GraphQL?



いいえ、もちろんです。 . , , GraphQL . GraphQL, . , , , . , , .



, GraphQL , , , . GraphQL , Apollo Relay, .



GraphQL — , , . graphql



( express-graphql



, ) — . , GraphQL - . , -, , , , .



まとめ



, GraphQL , . GraphQL , , , . , , , , GraphQL.



, : GraphQL . GraphQL . , GraphQL, , , , , , , .



— , GraphQL — , , . GraphQL , . , GraphQL — , , , . . , , , , , , GraphQL.



親愛なる読者! GraphQL — , .






All Articles