多くの場合、API から数値や文字列ではなく、独自の複雑な動作を持つオブジェクトを返したいと思うでしょう。GraphQL は、この目的に最適です。
GraphQL スキーマ言語では、新しいオブジェクト型を定義する方法は、これまでの例で `Query` 型を定義してきた方法と同じです。各オブジェクトは、特定の型を返すフィールドと、引数を取るメソッドを持つことができます。たとえば、引数の渡し方のドキュメントでは、ランダムなサイコロを振るメソッドがありました。
type Query { rollDice(numDice: Int!, numSides: Int): [Int]}
ランダムなサイコロに基づくメソッドを今後ますます増やしたい場合は、代わりに `RandomDie` オブジェクト型を使用して実装できます。
type RandomDie { roll(numRolls: Int!): [Int]}
type Query { getDie(numSides: Int): RandomDie}
`RandomDie` 型のルートレベルリゾルバーの代わりに、ES6 クラスを使用できます。この場合、リゾルバーはインスタンスメソッドになります。上記の `RandomDie` スキーマを実装する方法を次のコードに示します。
class RandomDie { constructor(numSides) { this.numSides = numSides }
rollOnce() { return 1 + Math.floor(Math.random() * this.numSides) }
roll({ numRolls }) { var output = [] for (var i = 0; i < numRolls; i++) { output.push(this.rollOnce()) } return output }}
var root = { getDie: ({ numSides }) => { return new RandomDie(numSides || 6) },}
引数を使用しないフィールドの場合、オブジェクトのプロパティまたはインスタンスメソッドのいずれかを使用できます。したがって、上記のサンプルコードでは、`numSides` と `rollOnce` の両方を使用して GraphQL フィールドを実装できます。そのため、このコードは次のスキーマも実装しています。
type RandomDie { numSides: Int! rollOnce: Int! roll(numRolls: Int!): [Int]}
type Query { getDie(numSides: Int): RandomDie}
これらをすべてまとめると、この GraphQL API を使用してサーバーを実行するサンプルコードは次のようになります。
var express = require("express")var { createHandler } = require("graphql-http/lib/use/express")var { buildSchema } = require("graphql")
// Construct a schema, using GraphQL schema languagevar schema = buildSchema(` type RandomDie { numSides: Int! rollOnce: Int! roll(numRolls: Int!): [Int] }
type Query { getDie(numSides: Int): RandomDie }`)
// This class implements the RandomDie GraphQL typeclass RandomDie { constructor(numSides) { this.numSides = numSides }
rollOnce() { return 1 + Math.floor(Math.random() * this.numSides) }
roll({ numRolls }) { var output = [] for (var i = 0; i < numRolls; i++) { output.push(this.rollOnce()) } return output }}
// The root provides the top-level API endpointsvar root = { getDie: ({ numSides }) => { return new RandomDie(numSides || 6) },}
var app = express()app.all( "/graphql", createHandler({ schema: schema, rootValue: root, }))app.listen(4000)console.log("Running a GraphQL API server at localhost:4000/graphql")
オブジェクト型を返す API に対して GraphQL クエリを発行する場合、GraphQL フィールド名をネストすることで、オブジェクトに対して複数のメソッドを一度に呼び出すことができます。たとえば、`rollOnce` を呼び出してサイコロを 1 回振り、`roll` を呼び出してサイコロを 3 回振りたい場合は、次のクエリを使用できます。
{ getDie(numSides: 6) { rollOnce roll(numRolls: 3) }}
このコードを `node server.js` で実行し、http://localhost:4000/graphql をブラウザで開くと、GraphiQL を使用してこれらの API を試すことができます。
このオブジェクト型の定義方法は、従来の REST API に比べて多くの利点があります。オブジェクトに関する基本情報を取得するための API リクエストを 1 つ行い、その後、そのオブジェクトに関する詳細情報を取得するために複数の API リクエストを行う代わりに、1 つの API リクエストですべての情報を取得できます。これにより、帯域幅が節約され、アプリの実行速度が向上し、クライアント側のロジックが簡素化されます。
これまで見てきた API はすべて、データを返すように設計されています。保存されているデータを変更したり、複雑な入力を処理したりするには、ミューテーションと入力型について学ぶと役立ちます。