

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# GraphQL の Interface と Union
<a name="interfaces-and-unions"></a>

GraphQL 型システムは [Interface](https://graphql.org/learn/schema/#interfaces) をサポートしています。インターフェイスを実装するためにタイプに含める必要がある、フィールドの特定セットをインターフェイスで公開します。

GraphQL 型システムは[Union](https://graphql.org/learn/schema/#union-types)もサポートしています。Union はインターフェイスと同じです。異なるのはフィールドの共通セットを定義しない点です。Union は、可能なタイプが論理階層を共有しないとき、一般的にインターフェイスより優先されます。

次のセクションは、スキーマの型付けに関するリファレンスです。

## インターフェースの例
<a name="interfaces"></a>

たとえば、何らかのアクティビティまたは人の集まりを `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 の [フラグメント](https://graphql.org/learn/queries/#fragments)を使用して、タイプを指定している限り、任意の実装タイプのフィールドにアクセスできます。

インターフェイスを使用する 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 つのコレクションとして返されるため、共通特性を表すためにインターフェイスを使用すると、結果のソートに大変役立ちます。

## ユニオンの例
<a name="unions"></a>

先に述べたように、ユニオンは共通のフィールドセットを定義しません。検索結果で多様なタイプを表すことがあります。`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 でのタイプ解決
<a name="type-resolution-in-appsynclong"></a>

タイプ解決は、GraphQL エンジンが特定のオブジェクトタイプとして解決された値を識別するメカニズムです。

ユニオン検索例に戻り、クエリで結果を出した場合、結果リストの各項目は、定義された `SearchResult` ユニオンの可能なタイプの 1 つ (つまり、`Conference`、`Festival`、`Concert`、または `Venue`) として、それ自体を示す必要があります。

`Festival` を `Venue` や `Conference` から識別するロジックは、アプリケーションの要件に依存するため、GraphQL エンジンには、そのままの結果から可能なタイプを識別するヒントを与える必要があります。

 AWS AppSync では、このヒントは という名前のメタフィールドで表されます。このメタフィールドの値は`__typename`、識別されたオブジェクトタイプ名に対応します。 `__typename`は、インターフェイスまたはユニオンである戻り型に必要です。

## タイプ解決の例
<a name="type-resolution-example"></a>

前のスキーマを再利用します。コンソールに移動して、[**スキーマ**] ページで以下を追加することで、手順どおりに進めることができます。

```
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` を識別できます。最終的に、使用するロジックを定義するのはユーザーです。