

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Interfaces et unions dans GraphQL
<a name="interfaces-and-unions"></a>

[Le système de type GraphQL prend en charge les interfaces.](https://graphql.org/learn/schema/#interfaces) Une interface expose un certain ensemble de champs qu'un type doit inclure pour implémenter l'interface. 

[Le système de type GraphQL prend également en charge les unions.](https://graphql.org/learn/schema/#union-types) Les unions sont identiques aux interfaces, si ce n'est qu'elles ne définissent pas un ensemble commun de champs. Les unions sont généralement préférées aux interfaces lorsque les types possibles ne partagent pas une hiérarchie logique.

La section suivante est une référence pour la saisie de schémas.

## Exemples d'interface
<a name="interfaces"></a>

Nous pourrions représenter une `Event` interface représentant tout type d'activité ou de rassemblement de personnes. Certains types d'événements possibles sont `Concert``Conference`, et`Festival`. Ces types partagent tous des caractéristiques communes : ils ont tous un nom, un lieu où l'événement se déroule, ainsi qu'une date de début et une date de fin. Ces types présentent également des différences ; un `Conference` propose une liste de conférenciers et d'ateliers, tandis qu'un `Concert` propose un groupe de musique.

Dans le langage SDL (Schema Definition Language), l'`Event`interface est définie comme suit :

```
interface Event {
        id: ID!
        name : String!
        startsAt: String
        endsAt: String
        venue: Venue
        minAgeRestriction: Int
}
```

Et chacun des types implémente l'`Event`interface comme suit :

```
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]
}
```

Les interfaces sont utiles pour représenter des éléments qui peuvent être de plusieurs types. Par exemple, nous pourrions rechercher tous les événements se déroulant dans un lieu spécifique. Ajoutons un champ `findEventsByVenue` au schéma :

```
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`Renvoie une liste de`Event`. Comme les champs d'interface GraphQL sont communs à tous les types d'implémentation, il est possible de sélectionner tous les champs de l'interface `Event` (`id`, `name`, `startsAt`, `endsAt`, `venue` et `minAgeRestriction`). En outre, vous pouvez accéder aux champs sur n'importe quel type d'implémentation en utilisant des [fragments](https://graphql.org/learn/queries/#fragments) GraphQL, tant que vous en spécifiez le type.

Examinons un exemple de requête GraphQL utilisant l'interface.

```
query {
  findEventsAtVenue(venueId: "Madison Square Garden") {
    id
    name
    minAgeRestriction
    startsAt

    ... on Festival {
      performers
    }

    ... on Concert {
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

La requête précédente génère une liste unique de résultats et le serveur pourrait, par défaut, trier les événements par date de début.

```
{
  "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"
        ]
      }
    ]
  }
}
```

Les résultats étant renvoyés sous la forme d'un ensemble unique d'événements, l'utilisation d'interfaces pour représenter des caractéristiques communes est très utile pour trier les résultats.

## Exemples syndicaux
<a name="unions"></a>

Comme indiqué précédemment, les syndicats ne définissent pas d'ensembles de domaines communs. Un résultat de recherche peut représenter de nombreux types différents. À l'aide du schéma `Event`, vous pouvez définir une union `SearchResult` comme suit :

```
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
```

Dans ce cas, pour interroger n'importe quel champ de notre `SearchResult` union, vous devez utiliser des fragments :

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
      id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

## Tapez la résolution dans AWS AppSync
<a name="type-resolution-in-appsynclong"></a>

La résolution de type est le mécanisme par lequel le moteur GraphQL identifie une valeur résolue en tant que type d'objet spécifique.

Pour en revenir à l'exemple de recherche syndicale, à condition que notre requête produise des résultats, chaque élément de la liste des résultats doit se présenter comme l'un des types possibles définis par l'`SearchResult`union (c'est-à-dire,`Conference`, `Festival``Concert`, ou`Venue`).

Comme la logique permettant d'identifier un événement `Festival` d'un élément `Venue` ou `Conference` dépend des exigences de l'application, le moteur GraphQL doit se voir attribuer un conseil, pour identifier nos types possibles parmi les résultats bruts.

Avec AWS AppSync, cet indice est représenté par un champ méta nommé`__typename`, dont la valeur correspond au nom du type d'objet identifié. `__typename`est obligatoire pour les types de retour qui sont des interfaces ou des unions.

## Exemple de résolution de type
<a name="type-resolution-example"></a>

Réutilisons le schéma précédent. Vous pouvez suivre en accédant à la console et en ajoutant les éléments suivants sous la page **Schéma** :

```
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
}
```

Attachons un résolveur au champ `Query.search`. Dans la `Resolvers` section, choisissez **Joindre**, créez une nouvelle **source de données** de type *NONE*, puis nommez-la *StubDataSource*. Dans le cadre de cet exemple, nous allons prétendre que nous avons récupéré des résultats d'une source externe, et coder les résultats récupérés dans notre modèle de mappage de requête.

Dans le volet du modèle de mappage de requête, saisissez :

```
{
    "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"]
        }
    ]
}
```

Si l'application renvoie le nom du type dans le `id` champ, la logique de résolution de type doit analyser le `id` champ pour extraire le nom du type, puis ajouter le `__typename` champ à chacun des résultats. Vous pouvez exécuter cette logique dans le modèle de mappage de réponse comme suit :

**Note**  
Vous pouvez également effectuer cette tâche dans le cadre de votre fonction Lambda, si vous utilisez la source de données 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)
```

Exécutez la requête suivante :

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
        id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

La requête produit les résultats suivants :

```
{
  "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"
        ]
      }
    ]
  }
}
```

La logique de résolution de type varie en fonction de l'application. Par exemple, vous pouvez avoir une logique d'identification différente qui recherche l'existence de certains champs ou même une combinaison de champs. En d'autres termes, vous pourriez détecter la présence du champ `performers` pour identifier un événement `Festival` ou la combinaison des champs `speakers` et `workshops` pour identifier un événement `Conference`. En fin de compte, c'est à vous de définir la logique que vous souhaitez utiliser.