GraphQL の Interface と Union
GraphQL 型システムは Interface
GraphQL 型システムはUnion
次のセクションは、スキーマの型付けに関するリファレンスです。
インターフェースの例
たとえば、何らかのアクティビティまたは人の集まりを Event
インターフェイスで表すことができます。考えられるイベントタイプにはConcert
、Conference
、Festival
などがあります。このようなタイプにはすべて、共通の特性 (名前、イベントの開催場所、開始日および終了日など) があります。これらのタイプに差分もあります。Conference
には、講演者やワークショップのリストがあり、Concert
には演奏するバンドが記載されています。
スキーマ定義言語 (SDL) では、Event
インターフェイスは次のように定義されます。
interface Event { id: ID! name : String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int }
また、Event
インターフェイスを実装する各タイプは次のようになります。
type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] }
インターフェイスは数種類の要素がある場合を表すのに便利です。たとえば、特定の会場で開催されているすべてのイベントを検索できます。次のように findEventsByVenue
フィールドをスキーマに追加します。
schema { query: Query } type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] } type Venue { id: ID! name: String address: String maxOccupancy: Int } type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String } interface Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] }
findEventsByVenue
は Event
のリストを返します。GraphQL インターフェイスフィールドはすべての実装タイプに共通しているため、Event
インターフェイス (id
、name
、startsAt
、endsAt
、venue
、および minAgeRestriction
) の任意のフィールドを選択できます。さらに、GraphQL の フラグメント
インターフェイスを使用する GraphQL クエリの例を詳しく見てみましょう。
query { findEventsAtVenue(venueId: "Madison Square Garden") { id name minAgeRestriction startsAt ... on Festival { performers } ... on Concert { performingBand } ... on Conference { speakers workshops } } }
上記のクエリは、結果の単一のリストを生み出し、サーバーで、デフォルトでは、開始日によりイベントをソートできます。
{ "data": { "findEventsAtVenue": [ { "id": "Festival-2", "name": "Festival 2", "minAgeRestriction": 21, "startsAt": "2018-10-05T14:48:00.000Z", "performers": [ "The Singers", "The Screamers" ] }, { "id": "Concert-3", "name": "Concert 3", "minAgeRestriction": 18, "startsAt": "2018-10-07T14:48:00.000Z", "performingBand": "The Jumpers" }, { "id": "Conference-4", "name": "Conference 4", "minAgeRestriction": null, "startsAt": "2018-10-09T14:48:00.000Z", "speakers": [ "The Storytellers" ], "workshops": [ "Writing", "Reading" ] } ] } }
結果はイベントの 1 つのコレクションとして返されるため、共通特性を表すためにインターフェイスを使用すると、結果のソートに大変役立ちます。
ユニオンの例
先に述べたように、ユニオンは共通のフィールドセットを定義しません。検索結果で多様なタイプを表すことがあります。Event
スキーマを使用して、次のように SearchResult
ユニオンを定義できます。
type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] # Search across all content search(query: String!): [SearchResult] } union SearchResult = Conference | Festival | Concert | Venue
この場合、SearchResult
ユニオンで任意のフィールドをクエリするには、フラグメントを使用する必要があります。
query { search(query: "Madison") { ... on Venue { id name address } ... on Festival { id name performers } ... on Concert { id name performingBand } ... on Conference { speakers workshops } } }
AWS AppSync でのタイプ解決
タイプ解決は、GraphQL エンジンが特定のオブジェクトタイプとして解決された値を識別するメカニズムです。
ユニオン検索例に戻り、クエリで結果を出した場合、結果リストの各項目は、定義された SearchResult
ユニオンの可能なタイプの 1 つ (つまり、Conference
、Festival
、Concert
、または Venue
) として、それ自体を示す必要があります。
Festival
を Venue
や Conference
から識別するロジックは、アプリケーションの要件に依存するため、GraphQL エンジンには、そのままの結果から可能なタイプを識別するヒントを与える必要があります。
AWS AppSync では、このヒントは __typename
という名前のメタフィールドで表されます。その値は、特定されたオブジェクトタイプ名に対応しています。戻り値の型がインターフェイスまたはユニオンに対して __typename
が必要です。
タイプ解決の例
前のスキーマを再利用します。コンソールに移動して、[スキーマ] ページで以下を追加することで、手順どおりに進めることができます。
schema { query: Query } type Query { # Retrieve Events at a specific Venue findEventsAtVenue(venueId: ID!): [Event] # Search across all content search(query: String!): [SearchResult] } union SearchResult = Conference | Festival | Concert | Venue type Venue { id: ID! name: String! address: String maxOccupancy: Int } interface Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int } type Festival implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performers: [String] } type Conference implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int speakers: [String] workshops: [String] } type Concert implements Event { id: ID! name: String! startsAt: String endsAt: String venue: Venue minAgeRestriction: Int performingBand: String }
リゾルバーを Query.search
フィールドにアタッチしましょう。Resolvers
セクションで、[アタッチ] を選択し、NONE タイプの新しいデータソースを作成してから、StubDataSource という名前を付けます。この例では、外部ソースから結果を取得したものとし、リクエストマッピングテンプレートで取得された結果をハードコードします。
リクエストマッピングテンプレートペインで、次のように入力します。
{ "version" : "2018-05-29", "payload": ## We are effectively mocking our search results for this example [ { "id": "Venue-1", "name": "Venue 1", "address": "2121 7th Ave, Seattle, WA 98121", "maxOccupancy": 1000 }, { "id": "Festival-2", "name": "Festival 2", "performers": ["The Singers", "The Screamers"] }, { "id": "Concert-3", "name": "Concert 3", "performingBand": "The Jumpers" }, { "id": "Conference-4", "name": "Conference 4", "speakers": ["The Storytellers"], "workshops": ["Writing", "Reading"] } ] }
アプリケーションが、id
フィールドの一部としてタイプ名を返す場合、タイプ解決ロジックは、id
フィールドを解析してタイプ名を抽出し、__typename
フィールドを各結果を追加します。次のようにレスポンスマッピングテンプレートでそのロジックを実行できます。
注記
Lambda データソースを使用している場合は、このタスクを Lambda 関数の一部として実行することもできます。
#foreach ($result in $context.result) ## Extract type name from the id field. #set( $typeName = $result.id.split("-")[0] ) #set( $ignore = $result.put("__typename", $typeName)) #end $util.toJson($context.result)
次のクエリを実行します。
query { search(query: "Madison") { ... on Venue { id name address } ... on Festival { id name performers } ... on Concert { id name performingBand } ... on Conference { speakers workshops } } }
クエリにより、次の結果が返されます。
{ "data": { "search": [ { "id": "Venue-1", "name": "Venue 1", "address": "2121 7th Ave, Seattle, WA 98121" }, { "id": "Festival-2", "name": "Festival 2", "performers": [ "The Singers", "The Screamers" ] }, { "id": "Concert-3", "name": "Concert 3", "performingBand": "The Jumpers" }, { "speakers": [ "The Storytellers" ], "workshops": [ "Writing", "Reading" ] } ] } }
タイプ解決ロジックはアプリケーションによって異なります。たとえば、特定のフィールドまたはフィールドの組み合わせの存在をチェックする、異なる識別ロジックがあってもかまいません。つまり、performers
フィールドの存在を検出して、Festival
を識別する、または speakers
と workshops
フィールドの組み合わせを検出して、Conference
を識別できます。最終的に、使用するロジックを定義するのはユーザーです。