

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.

# Tutoriels de résolution VTL pour AWS AppSync
<a name="tutorials"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Les sources de données et les résolveurs sont utilisés AWS AppSync pour traduire les requêtes GraphQL et récupérer des informations à partir de vos ressources. AWS AWS AppSync prend en charge le provisionnement automatique et les connexions avec certains types de sources de données. AWS AppSync prend également en charge Amazon DynamoDB AWS Lambda, les bases de données relationnelles (Amazon Aurora Serverless), OpenSearch Amazon Service et les points de terminaison HTTP en tant que sources de données. Vous pouvez utiliser une API GraphQL avec vos AWS ressources existantes ou créer des sources de données et des résolveurs à partir de zéro. Les sections suivantes visent à élucider certains des cas d'utilisation les plus courants de GraphQL sous forme de didacticiels.

AWS AppSync utilise des *modèles de mappage* écrits en langage Apache Velocity Template Language (VTL) pour les résolveurs. Pour plus d'informations sur l'utilisation des modèles de mappage, consultez la [référence des modèles de mappage Resolver](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference). De plus amples informations sur l'utilisation de VTL sont disponibles dans le guide de [programmation du modèle de mappage Resolver](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide).

AWS AppSync prend en charge le provisionnement automatique des tables DynamoDB à partir d'un schéma GraphQL, comme décrit dans Provisionner à partir d'un schéma (facultatif) et Lancer un exemple de schéma. Vous pouvez également importer à partir d'une table DynamoDB existante qui va créer les schémas et connecter les résolveurs. Ceci est décrit dans Importer depuis Amazon DynamoDB (facultatif).

**Topics**
+ [Création d'une post-application simple à l'aide des résolveurs DynamoDB](tutorial-dynamodb-resolvers.md)
+ [Utiliser des AWS Lambda résolveurs](tutorial-lambda-resolvers.md)
+ [Utilisation des résolveurs OpenSearch de service](tutorial-elasticsearch-resolvers.md)
+ [Utilisation de résolveurs locaux](tutorial-local-resolvers.md)
+ [Combinaison de résolveurs GraphQL](tutorial-combining-graphql-resolvers.md)
+ [Utilisation des opérations par lots DynamoDB](tutorial-dynamodb-batch.md)
+ [Exécution de transactions DynamoDB](tutorial-dynamodb-transact.md)
+ [Utilisation de résolveurs HTTP](tutorial-http-resolvers.md)
+ [Utilisation des résolveurs Aurora Serverless v2](tutorial-rds-resolvers.md)
+ [Utilisation de résolveurs de pipeline](tutorial-pipeline-resolvers.md)
+ [Utilisation des opérations Delta Sync sur des sources de données versionnées](tutorial-delta-sync.md)

# Création d'une post-application simple à l'aide des résolveurs DynamoDB
<a name="tutorial-dynamodb-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Ce didacticiel explique comment importer vos propres tables Amazon DynamoDB et les connecter AWS AppSync à une API GraphQL.

Vous pouvez autoriser le AWS AppSync provisionnement des ressources DynamoDB en votre nom. Ou, si vous préférez, vous pouvez connecter vos tables existantes à un schéma GraphQL en créant une source de données et un résolveur. Dans les deux cas, vous serez en mesure de lire et d'écrire dans votre base de données DynamoDB via les instructions GraphQL, et de vous abonner aux données en temps réel.

Certaines étapes de configuration spécifiques doivent être effectuées pour convertir des instructions GraphQL en opérations DynamoDB, et pour reconvertir les réponses dans GraphQL. Ce didacticiel explique le processus de configuration par le biais de plusieurs scénarios et modèles d'accès aux données concrets.

## Configuration de vos tables DynamoDB
<a name="setting-up-your-ddb-tables"></a>

Pour commencer ce didacticiel, vous devez d'abord suivre les étapes ci-dessous pour provisionner AWS les ressources.

1. Provisionnez les AWS ressources à l'aide du AWS CloudFormation modèle suivant dans la CLI :

   ```
   aws cloudformation create-stack \
       --stack-name AWSAppSyncTutorialForAmazonDynamoDB \
       --template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml \
       --capabilities CAPABILITY_NAMED_IAM
   ```

   Vous pouvez également lancer la CloudFormation pile suivante dans la région US-West 2 (Oregon) sur votre AWS compte.

   [https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml)

   Cela crée les éléments suivants :
   + Une table DynamoDB `AppSyncTutorial-Post` appelée qui contiendra les données. `Post`
   + Un rôle IAM et une politique gérée IAM associée permettant d' AWS AppSync interagir avec la `Post` table.

1. Pour plus de détails sur la pile et les ressources créées, exécutez la commande d'interface de ligne de commande suivante :

   ```
   aws cloudformation describe-stacks --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

1. Pour supprimer ultérieurement les ressources, vous pouvez exécuter :

   ```
   aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

## Création de votre API GraphQL
<a name="creating-your-graphql-api"></a>

Pour créer l'API GraphQL dans : AWS AppSync

1. Connectez-vous à la [AppSync console AWS Management Console et ouvrez-la](https://console.aws.amazon.com/appsync/).

   1. Dans le **APIs tableau de bord**, choisissez **Create API**.

1. Dans la fenêtre **Personnaliser votre API ou importer depuis Amazon DynamoDB**, **choisissez** Créer à partir de zéro.

   1. Choisissez **Démarrer** à droite de la même fenêtre.

1. Dans le champ **Nom de l'API**, définissez le nom de l'API sur`AWSAppSyncTutorial`.

1. Choisissez **Créer**.

La AWS AppSync console crée une nouvelle API GraphQL pour vous en utilisant le mode d'authentification par clé d'API. Vous pouvez utiliser la console pour configurer le reste de l'API GraphQL et exécuter des requêtes sur celle-ci jusqu'à la fin de ce didacticiel.

## Définition d'une API de publication de base
<a name="defining-a-basic-post-api"></a>

Maintenant que vous avez créé une API AWS AppSync GraphQL, vous pouvez configurer un schéma de base qui permet la création, la récupération et la suppression de base des données de publication.

1. Connectez-vous à la [AppSync console AWS Management Console et ouvrez-la](https://console.aws.amazon.com/appsync/).

   1. Dans le **APIs tableau de bord**, choisissez l'API que vous venez de créer.

1. Dans la **barre latérale**, choisissez **Schema.**

   1. Dans le volet **Schéma**, remplacez le contenu par le code suivant :

     ```
     schema {
         query: Query
         mutation: Mutation
     }
     
     type Query {
         getPost(id: ID): Post
     }
     
     type Mutation {
         addPost(
             id: ID!
             author: String!
             title: String!
             content: String!
             url: String!
         ): Post!
     }
     
     type Post {
         id: ID!
         author: String
         title: String
         content: String
         url: String
         ups: Int!
         downs: Int!
         version: Int!
     }
     ```

1. Choisissez **Enregistrer**.

Ce schéma définit un type `Post` et des opérations pour ajouter et obtenir des objets `Post`.

## Configuration de la source de données pour les tables DynamoDB
<a name="configuring-the-data-source-for-the-ddb-tables"></a>

Liez ensuite les requêtes et les mutations définies dans le schéma à la table `AppSyncTutorial-Post` DynamoDB.

Tout d' AWS AppSync abord, vous devez connaître vos tables. Pour ce faire, vous devez configurer une source de données dans AWS AppSync :

1. Connectez-vous à la [AppSync console AWS Management Console et ouvrez-la](https://console.aws.amazon.com/appsync/).

   1. Dans le **APIs tableau de bord**, choisissez votre API GraphQL.

   1. Dans la **barre latérale**, sélectionnez **Sources de données.**

1. Choisissez **Create data source**.

   1. Dans le **champ Nom de la source de données**, entrez`PostDynamoDBTable`. 

   1. Pour le **type de source de données**, choisissez la **table Amazon DynamoDB**.

   1. Pour **Région**, choisissez **US-WEST-2**.

   1. Pour **Nom de la table**, choisissez la table **AppSyncTutorialDynamoDB -Post**.

   1. Créez un nouveau rôle IAM (recommandé) ou choisissez un rôle existant disposant de l'autorisation `lambda:invokeFunction` IAM. Les rôles existants nécessitent une politique de confiance, comme expliqué dans la section [Joindre une source de données](attaching-a-data-source.md). 

      Voici un exemple de politique IAM disposant des autorisations requises pour effectuer des opérations sur la ressource :

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction", 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. Choisissez **Créer**.

## Configuration du résolveur AddPost (DynamoDB) PutItem
<a name="setting-up-the-addpost-resolver-dynamodb-putitem"></a>

**Une fois AWS AppSync que vous avez pris connaissance de la table DynamoDB, vous pouvez la lier à des requêtes et à des mutations individuelles en définissant des résolveurs.** Le premier résolveur que vous créez est le `addPost` résolveur, qui vous permet de créer une publication dans la table DynamoDB`AppSyncTutorial-Post`.

Un résolveur comprend les éléments suivants :
+ L'emplacement dans le schéma GraphQL pour joindre le résolveur. Dans ce cas, vous configurez un résolveur sur le champ `addPost` sur le type `Mutation`. Ce résolveur est appelé lorsque l'appelant appelle `mutation { addPost(...){...} }`.
+ La source de données à utiliser pour ce résolveur. Dans ce cas, vous souhaitez utiliser la source de données `PostDynamoDBTable` que vous avez définie précédemment, afin de pouvoir ajouter des entrées dans la table DynamoDB `AppSyncTutorial-Post`.
+ Le modèle de mappage de demande. L'objectif du modèle de mappage des demandes est de prendre la demande entrante de l'appelant et de la traduire en instructions AWS AppSync à exécuter par rapport à DynamoDB.
+ Le modèle de mappage de réponse. La tâche du modèle de mappage de réponse est de prendre la réponse provenant de DynamoDB et de la reconvertir en quelque chose que GraphQL attend. Cela est utile si la forme des données dans DynamoDB est différente du type `Post` dans GraphQL, mais dans ce cas, ils ont la même forme, de sorte que vous transmettez simplement les données.

Pour configurer le résolveur :

1. Connectez-vous à la [AppSync console AWS Management Console et ouvrez-la](https://console.aws.amazon.com/appsync/).

   1. Dans le **APIs tableau de bord**, choisissez votre API GraphQL.

   1. Dans la **barre latérale**, sélectionnez **Sources de données.**

1. Choisissez **Create data source**.

   1. Dans le **champ Nom de la source de données**, entrez`PostDynamoDBTable`. 

   1. Pour le **type de source de données**, choisissez la **table Amazon DynamoDB**.

   1. Pour **Région**, choisissez **US-WEST-2**.

   1. Pour **Nom de la table**, choisissez la table **AppSyncTutorialDynamoDB -Post**.

   1. Créez un nouveau rôle IAM (recommandé) ou choisissez un rôle existant disposant de l'autorisation `lambda:invokeFunction` IAM. Les rôles existants nécessitent une politique de confiance, comme expliqué dans la section [Joindre une source de données](attaching-a-data-source.md). 

      Voici un exemple de politique IAM disposant des autorisations requises pour effectuer des opérations sur la ressource :

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction", 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. Choisissez **Créer**.

1. Choisissez l'onglet **Schéma**.

1. Dans le volet **Types de données** sur la droite, se trouve le champ **addPost** sur le type **Mutation**, choisissez ensuite **Joindre**.

1. Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).

1. Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.

1. Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

   ```
   {
       "version" : "2017-02-28",
       "operation" : "PutItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
       "attributeValues" : {
           "author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
           "title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
           "content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
           "url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
           "ups" : { "N" : 1 },
           "downs" : { "N" : 0 },
           "version" : { "N" : 1 }
       }
   }
   ```

   **Remarque :** un *type* est spécifié sur toutes les clés et valeurs d'attribut. Par exemple, vous définissez le champ `author` sur `{ "S" : "${context.arguments.author}" }`. La `S` partie indique à DynamoDB AWS AppSync et à DynamoDB que la valeur sera une valeur de chaîne. La valeur réelle est renseignée à partir de l'argument `author`. De la même façon, le champ `version` est un champ numérique, car il utilise `N` comme type. Enfin, vous initialisez également les champs `ups`, `downs` et `version`.

   Pour ce didacticiel, vous avez indiqué que le `ID!` type GraphQL, qui indexe le nouvel élément inséré dans DynamoDB, fait partie des arguments du client. AWS AppSync est livré avec un utilitaire de génération automatique d'identifiants appelé `$utils.autoId()` que vous auriez également pu utiliser sous la forme de`"id" : { "S" : "${$utils.autoId()}" }`. Ensuite, vous pourriez simplement laisser `id: ID!` hors de la définition de schéma de `addPost()` et il serait inséré automatiquement. Vous n'utiliserez pas cette technique dans ce didacticiel, mais vous devez la considérer comme une bonne pratique lorsque vous écrivez dans des tables DynamoDB.

   Pour plus d'informations sur les modèles de mappage, consultez la documentation de référence [Présentation des modèles de mappage des résolveurs](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview). Pour plus d'informations sur le mappage des GetItem demandes, consultez la documentation de [GetItem](aws-appsync-resolver-mapping-template-reference-dynamodb-getitem.md)référence. Pour plus d'informations sur les types, consultez la documentation de référence [Système de types (mappage de demande)](aws-appsync-resolver-mapping-template-reference-dynamodb-typed-values-request.md).

1. Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

   ```
   $utils.toJson($context.result)
   ```

    **Remarque :** comme la forme des données de la table `AppSyncTutorial-Post` correspond exactement à la forme du type `Post` dans GraphQL, le modèle de mappage de réponse transmet simplement directement les résultats. Notez également que tous les exemples utilisés dans ce tutoriel utilisent le même modèle de mappage de réponse, si bien que vous ne créez qu'un fichier.

1. Choisissez **Enregistrer**.

### Appel de l'API pour ajouter une publication
<a name="call-the-api-to-add-a-post"></a>

Maintenant que le résolveur est configuré, il AWS AppSync peut traduire une `addPost` mutation entrante en une opération DynamoDB PutItem . Vous pouvez désormais exécuter une mutation pour placer quelque chose dans la table.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**.

  ```
  mutation addPost {
    addPost(
      id: 123
      author: "AUTHORNAME"
      title: "Our first post!"
      content: "This is our first post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Les résultats de la publication nouvellement créée doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

Voici ce qui s'est produit :
+ AWS AppSync a reçu une demande de `addPost` mutation.
+ AWS AppSync a pris la demande et le modèle de mappage des demandes, et a généré un document de mappage des demandes. Celui-ci se présentait comme suit :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "PutItem",
      "key" : {
          "id" : { "S" : "123" }
      },
      "attributeValues" : {
          "author": { "S" : "AUTHORNAME" },
          "title": { "S" : "Our first post!" },
          "content": { "S" : "This is our first post." },
          "url": { "S" : "https://aws.amazon.com/appsync/" },
          "ups" : { "N" : 1 },
          "downs" : { "N" : 0 },
          "version" : { "N" : 1 }
      }
  }
  ```
+ AWS AppSync a utilisé le document de mappage des demandes pour générer et exécuter une demande `PutItem` DynamoDB.
+ AWS AppSync a pris les résultats de la `PutItem` requête et les a reconvertis en types GraphQL.

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ Ils ont été transmis via le document de mappage de réponse, qui les a transmis sans les changer.
+ L'objet nouvellement créé a été renvoyé dans la réponse GraphQL.

## Configuration du résolveur GetPost (DynamoDB) GetItem
<a name="setting-up-the-getpost-resolver-ddb-getitem"></a>

Maintenant que vous pouvez ajouter des données à la table `AppSyncTutorial-Post` DynamoDB, vous devez configurer `getPost` la requête afin qu'elle puisse récupérer ces données de la table. `AppSyncTutorial-Post` Pour ce faire, vous configurez un autre résolveur.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Types de données** sur la droite, cherchez le champ **getPost** sur le type **Requête**, puis choisissez **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
      }
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.

### Appel de l'API pour obtenir une publication
<a name="call-the-api-to-get-a-post"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `getPost` requête entrante en une opération DynamoDB`GetItem`. Vous pouvez désormais exécuter une requête pour récupérer la publication que vous avez créée précédemment.
+ Choisissez l'onglet **Requêtes**.
+ Collez ce qui suit dans le volet **Requêtes** :

  ```
  query getPost {
    getPost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication extraite de DynamoDB doit apparaître dans le volet des résultats à droite du volet des requêtes. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "getPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

Voici ce qui s'est produit :
+ AWS AppSync a reçu une demande de `getPost` requête.
+ AWS AppSync a pris la demande et le modèle de mappage des demandes, et a généré un document de mappage des demandes. Celui-ci se présentait comme suit :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : { "S" : "123" }
      }
  }
  ```
+ AWS AppSync a utilisé le document de mappage des demandes pour générer et exécuter une demande GetItem DynamoDB.
+ AWS AppSync a pris les résultats de la `GetItem` requête et les a reconvertis en types GraphQL.

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ Ils ont été transmis via le document de mappage de réponse, qui les a transmis sans les changer.
+ L'objet récupéré a été renvoyé dans la réponse.

Vous pouvez également prendre l'exemple suivant :

```
query getPost {
  getPost(id:123) {
    id
    author
    title
  }
}
```

Si votre `getPost` requête n'a besoin que du`id`, et `author``title`, vous pouvez modifier votre modèle de mappage de demandes pour utiliser des expressions de projection afin de spécifier uniquement les attributs que vous souhaitez voir apparaître dans votre table DynamoDB afin d'éviter tout transfert de données inutile de DynamoDB vers. AWS AppSync Par exemple, le modèle de mappage des demandes peut ressembler à l'extrait ci-dessous :

```
{
    "version" : "2017-02-28",
    "operation" : "GetItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "projection" : {
     "expression" : "#author, id, title",
     "expressionNames" : { "#author" : "author"}
    }
}
```

## Création d'une mutation UpdatePost (DynamoDB) UpdateItem
<a name="create-an-updatepost-mutation-ddb-updateitem"></a>

Jusqu'à présent, vous pouvez créer et récupérer `Post` des objets dans DynamoDB. Ensuite, vous allez configurer une nouvelle mutation pour pouvoir mettre à jour l'objet. Pour ce faire, utilisez l'opération UpdateItem DynamoDB.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Mutation` pour ajouter une nouvelle mutation `updatePost` :

  ```
  type Mutation {
      updatePost(
          id: ID!,
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post
      addPost(
          author: String!
          title: String!
          content: String!
          url: String!
      ): Post!
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **updatePost** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "SET author = :author, title = :title, content = :content, #url = :url ADD version :one",
          "expressionNames": {
              "#url" : "url"
          },
          "expressionValues": {
              ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
              ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
              ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
              ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
              ":one" : { "N": 1 }
          }
      }
  }
  ```

   **Remarque :** Ce résolveur utilise le UpdateItem DynamoDB, qui est très différent de l'opération. PutItem Au lieu d'écrire l'élément dans son intégralité, vous demandez simplement à DynamoDB de mettre à jour certains attributs. Cela se fait à l'aide des expressions de mise à jour DynamoDB. L'expression elle-même est spécifiée dans le champ `expression` de la section `update`. Elle indique de définir les attributs `author`, `title`, `content` et URL, puis d'incrémenter le champ `version`. Les valeurs à utiliser n'apparaissent pas dans l'expression elle-même ; l'expression a des espaces réservés qui ont des noms qui commencent par deux points et qui sont ensuite définis dans le champ `expressionValues`. Enfin, DynamoDB a réservé des mots qui ne peuvent pas apparaître dans le. `expression` Par exemple, `url` est un mot réservé, si bien que pour mettre à jour le champ `url`, vous pouvez utiliser des espaces réservés de nom et les définir dans le champ `expressionNames`.

  Pour plus d'informations sur le mappage des `UpdateItem` demandes, consultez la documentation de [UpdateItem](aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem.md)référence. Pour plus d'informations sur l'écriture d'expressions de mise à jour, consultez la documentation [ UpdateExpressions DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```

### Appel de l'API pour mettre à jour une publication
<a name="call-the-api-to-update-a-post"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `update` mutation entrante en une opération DynamoDB`Update`. Vous pouvez désormais exécuter une mutation pour mettre à jour l'élément que vous avez écrit précédemment.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation updatePost {
    updatePost(
      id:"123"
      author: "A new author"
      title: "An updated author!"
      content: "Now with updated content!"
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication mise à jour dans DynamoDB doit apparaître dans le volet des résultats à droite du volet des requêtes. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An updated author!",
        "content": "Now with updated content!",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 2
      }
    }
  }
  ```

Dans cet exemple, les `downs` champs `ups` et n'ont pas été modifiés car le modèle de mappage des demandes n'a pas demandé à AWS AppSync DynamoDB de faire quoi que ce soit avec ces champs. De plus, le `version` champ a été incrémenté de 1 parce que vous avez demandé AWS AppSync à DynamoDB d'ajouter 1 au champ. `version`

## Modification du résolveur UpdatePost (DynamoDB) UpdateItem
<a name="modifying-the-updatepost-resolver-dynamodb-updateitem"></a>

Il s'agit d'un bon début pour la mutation `updatePost`, mais elle comporte deux problèmes principaux :
+ Si vous souhaitez mettre à jour un seul champ, vous devez mettre à jour tous les champs.
+ Si deux personnes modifient l'objet, des informations risquent d'être perdues.

Pour résoudre ces problèmes, vous allez modifier la mutation `updatePost` pour modifier uniquement les arguments qui ont été spécifiés dans la demande, puis ajouter une condition à l'opération `UpdateItem`.

1. Choisissez l'onglet **Schéma**.

1. Dans le volet **Schéma**, modifiez le champ `updatePost` dans le type `Mutation` pour supprimer les points d'exclamation des arguments `author`, `title`, `content`, et `url`, en veillant à laisser le champ `id` en l'état. Cela rend ces arguments facultatifs. En outre, ajoutez un nouvel argument `expectedVersion` requis.

   ```
   type Mutation {
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       addPost(
           author: String!
           title: String!
           content: String!
           url: String!
       ): Post!
   }
   ```

1. Choisissez **Enregistrer**.

1. Dans le volet **Types de données** de droite, cherchez le champ **updatePost** sur le type **Mutation**.

1. Choisissez **PostDynamoDBTable**d'ouvrir le résolveur existant.

1. Modifiez le modèle de mappage de demande dans la section **Configurer le modèle de mappage de demande** :

   ```
   {
       "version" : "2017-02-28",
       "operation" : "UpdateItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
   
       ## Set up some space to keep track of things you're updating **
       #set( $expNames  = {} )
       #set( $expValues = {} )
       #set( $expSet = {} )
       #set( $expAdd = {} )
       #set( $expRemove = [] )
   
       ## Increment "version" by 1 **
       $!{expAdd.put("version", ":one")}
       $!{expValues.put(":one", { "N" : 1 })}
   
       ## Iterate through each argument, skipping "id" and "expectedVersion" **
       #foreach( $entry in $context.arguments.entrySet() )
           #if( $entry.key != "id" && $entry.key != "expectedVersion" )
               #if( (!$entry.value) && ("$!{entry.value}" == "") )
                   ## If the argument is set to "null", then remove that attribute from the item in DynamoDB **
   
                   #set( $discard = ${expRemove.add("#${entry.key}")} )
                   $!{expNames.put("#${entry.key}", "$entry.key")}
               #else
                   ## Otherwise set (or update) the attribute on the item in DynamoDB **
   
                   $!{expSet.put("#${entry.key}", ":${entry.key}")}
                   $!{expNames.put("#${entry.key}", "$entry.key")}
                   $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })}
               #end
           #end
       #end
   
       ## Start building the update expression, starting with attributes you're going to SET **
       #set( $expression = "" )
       #if( !${expSet.isEmpty()} )
           #set( $expression = "SET" )
           #foreach( $entry in $expSet.entrySet() )
               #set( $expression = "${expression} ${entry.key} = ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to ADD **
       #if( !${expAdd.isEmpty()} )
           #set( $expression = "${expression} ADD" )
           #foreach( $entry in $expAdd.entrySet() )
               #set( $expression = "${expression} ${entry.key} ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to REMOVE **
       #if( !${expRemove.isEmpty()} )
           #set( $expression = "${expression} REMOVE" )
   
           #foreach( $entry in $expRemove )
               #set( $expression = "${expression} ${entry}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
       "update" : {
           "expression" : "${expression}"
           #if( !${expNames.isEmpty()} )
               ,"expressionNames" : $utils.toJson($expNames)
           #end
           #if( !${expValues.isEmpty()} )
               ,"expressionValues" : $utils.toJson($expValues)
           #end
       },
   
       "condition" : {
           "expression"       : "version = :expectedVersion",
           "expressionValues" : {
               ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
           }
       }
   }
   ```

1. Choisissez **Enregistrer**.

Ce modèle est l'un des exemples les plus complexes. Il illustre la puissance et la flexibilité des modèles de mappage. Il passe en revue tous les arguments, en faisant l'impasse sur `id` et `expectedVersion`. Si l'argument est défini sur quelque chose, il demande AWS AppSync à DynamoDB de mettre à jour cet attribut sur l'objet dans DynamoDB. Si l'attribut est défini sur null, il demande AWS AppSync à DynamoDB de le supprimer de l'objet de publication. Si un argument n'a pas été spécifié, il laisse l'attribut. Il incrémente également le champ `version`.

Il existe également une nouvelle section `condition`. Une expression de condition vous permet de dire AWS AppSync à DynamoDB si la demande doit aboutir ou non en fonction de l'état de l'objet déjà présent dans DynamoDB avant l'exécution de l'opération. Dans ce cas, vous souhaitez que la `UpdateItem` demande aboutisse uniquement si le `version` champ de l'élément actuellement dans DynamoDB correspond exactement à l'argument. `expectedVersion`

Pour plus d'informations sur les expressions de condition, consultez la documentation de référence [Expressions de condition](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md).

### Appel de l'API pour mettre à jour une publication
<a name="id1"></a>

Essayons de mettre à jour l'objet `Post` avec le nouveau résolveur :
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation updatePost {
    updatePost(
      id:123
      title: "An empty story"
      content: null
      expectedVersion: 2
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication mise à jour dans DynamoDB doit apparaître dans le volet des résultats à droite du volet des requêtes. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      }
    }
  }
  ```

Dans cette demande, vous avez demandé à DynamoDB de mettre à jour `title` le `content` champ AWS AppSync et uniquement. Cela a laissé seuls tous les autres champs (autres que l'incrémentation du champ `version`). Vous avez défini l'attribut `title` sur une nouvelle valeur et avons supprimé l'attribut `content` de la publication. Les champs `author`, `url`, `ups` et `downs` sont restés inchangés.

Essayez d'exécuter une nouvelle fois la demande de mutation en laissant la demande exactement telle quelle. La réponse devrait être similaire à ce qui suit :

```
{
  "data": {
    "updatePost": null
  },
  "errors": [
    {
      "path": [
        "updatePost"
      ],
      "data": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      },
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
    }
  ]
}
```

La demande échoue, car l'expression de condition prend la valeur false :
+ La première fois que vous avez exécuté la demande, la valeur du `version` champ de la publication dans DynamoDB `2` était, qui correspondait à l'argument. `expectedVersion` La demande a abouti, ce qui signifie que le `version` champ a été incrémenté dans DynamoDB à. `3`
+ La deuxième fois que vous avez exécuté la demande, la valeur du `version` champ de la publication dans DynamoDB `3` était, ce qui ne correspondait pas à l'argument. `expectedVersion`

Ce modèle est généralement appelé *Verrouillage optimiste*.

L'une des fonctionnalités d'un résolveur AWS AppSync DynamoDB est qu'il renvoie la valeur actuelle de l'objet de publication dans DynamoDB. Vous pouvez trouver cette valeur dans le champ `data` de la section `errors` de la réponse GraphQL. Votre application peut utiliser ces informations pour déterminer la façon dont elle doit continuer. Dans ce cas, vous pouvez voir que le `version` champ de l'objet dans DynamoDB est défini sur. Vous pouvez donc simplement mettre `3` à jour `expectedVersion` l'argument `3` pour que la demande aboutisse à nouveau.

Consultez la documentation de référence sur le modèle de mappage [Expressions de condition](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md) pour obtenir plus d'informations sur la gestion des échecs de vérification de condition.

## Création de mutations UpVotePost et DownVotePost (DynamoDB) UpdateItem
<a name="create-upvotepost-and-downvotepost-mutations-ddb-updateitem"></a>

Le type `Post` a des champs `ups` et `downs` pour permettre d'enregistrer les votes pour et contre, mais jusqu'à présent l'API ne nous laisse rien faire avec eux. Permet d'ajouter certaines mutations pour nous laisser voter pour ou contre les publications.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Mutation` pour ajouter de nouvelles mutations `upvotePost` et `downvotePost` :

  ```
  type Mutation {
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **upvotePost** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD ups :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ `downvotePost` qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD downs :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.

### Appel de l'API pour voter pour ou contre une publication
<a name="call-the-api-to-upvote-and-downvote-a-post"></a>

Maintenant que les nouveaux résolveurs ont été configurés, ils AWS AppSync savent comment traduire une opération entrante `upvotePost` ou une `downvote` mutation en opération DynamoDB UpdateItem . Vous pouvez désormais exécuter des mutations pour voter pour ou contre la publication que vous avez créée précédemment.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation votePost {
    upvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication est mise à jour dans DynamoDB et doit apparaître dans le volet des résultats à droite du volet des requêtes. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "upvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 0,
        "version": 4
      }
    }
  }
  ```
+ Choisissez **Exécuter une requête** quelque fois de plus. Vous devez voir le champ `ups` et `version` être incrémenté de 1 chaque fois que vous exécutez la requête.
+ Modifiez la requête pour appeler la mutation `downvotePost` :

  ```
  mutation votePost {
    downvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange). Cette fois, vous devez voir le champ `downs` et `version` être incrémenté de 1 chaque fois que vous exécutez la requête.

  ```
  {
    "data": {
      "downvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

## Configuration du résolveur DeletePost (DynamoDB) DeleteItem
<a name="setting-up-the-deletepost-resolver-ddb-deletepost"></a>

La prochaine mutation que vous allez configurer permettra de supprimer une publication. Pour ce faire, utilisez l'opération `DeleteItem` DynamoDB.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Mutation` pour ajouter une nouvelle mutation `deletePost` :

  ```
  type Mutation {
      deletePost(id: ID!, expectedVersion: Int): Post
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```

  Cette fois vous avez rendu le champ `expectedVersion` facultatif, ce qui est expliqué plus loin lorsque vous ajoutez le modèle de mappage de demande.
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **delete** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "DeleteItem",
      "key": {
          "id": $util.dynamodb.toDynamoDBJson($context.arguments.id)
      }
      #if( $context.arguments.containsKey("expectedVersion") )
          ,"condition" : {
              "expression"       : "attribute_not_exists(id) OR version = :expectedVersion",
              "expressionValues" : {
                  ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
              }
          }
      #end
  }
  ```

   **Remarque :** l'argument `expectedVersion` est un argument facultatif. Si l'appelant définit un `expectedVersion` argument dans la demande, le modèle ajoute une condition qui permet à la `DeleteItem` demande de réussir uniquement si l'élément est déjà supprimé ou si l'`version`attribut de la publication dans DynamoDB correspond exactement au. `expectedVersion` En cas d'omission, aucune expression de condition n'est spécifiée dans la demande `DeleteItem`. Il réussit quelle que soit la valeur de l'`version`élément ou qu'il existe ou non dans DynamoDB.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```

   **Remarque :** même si vous supprimez un élément, vous pouvez renvoyer l'élément qui a été supprimé, s'il n'était pas déjà supprimé.
+ Choisissez **Enregistrer**.

Pour plus d'informations sur le mappage des `DeleteItem` demandes, consultez la documentation de [DeleteItem](aws-appsync-resolver-mapping-template-reference-dynamodb-deleteitem.md)référence.

### Appel de l'API pour supprimer une publication
<a name="call-the-api-to-delete-a-post"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `delete` mutation entrante en une opération DynamoDB`DeleteItem`. Vous pouvez désormais exécuter une mutation pour supprimer quelque chose dans la table.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation deletePost {
    deletePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication est supprimée de DynamoDB. Notez que cela AWS AppSync renvoie la valeur de l'élément qui a été supprimé de DynamoDB, qui doit apparaître dans le volet des résultats à droite du volet des requêtes. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

La valeur est renvoyée seulement si cet appel à `deletePost` est celui qui l'a réellement supprimée de DynamoDB.
+ Choisissez **Exécuter une requête** à nouveau.
+ L'appel réussit quand même, mais aucune valeur n'est renvoyée.

  ```
  {
    "data": {
      "deletePost": null
    }
  }
  ```

Maintenant, essayons de supprimer une publication, mais cette fois en spécifiant `expectedValue`. Tout d'abord, vous devez créer une nouvelle publication, car vous venez de supprimer celle que vous utilisiez jusqu'à présent.
+ Collez la mutation suivante dans le volet **Requêtes**.

  ```
  mutation addPost {
    addPost(
      id:123
      author: "AUTHORNAME"
      title: "Our second post!"
      content: "A new post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Les résultats de la publication nouvellement créée doivent apparaître dans le volet des résultats à droite du volet de requête. Notez l'`id` de l'objet nouvellement créé, car vous en aurez besoin sous peu. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

Maintenant, essayons de supprimer cette publication, mais insérons une valeur incorrecte pour `expectedVersion` :
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 9999
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).

  ```
  {
    "data": {
      "deletePost": null
    },
    "errors": [
      {
        "path": [
          "deletePost"
        ],
        "data": {
          "id": "123",
          "author": "AUTHORNAME",
          "title": "Our second post!",
          "content": "A new post.",
          "url": "https://aws.amazon.com/appsync/",
          "ups": 1,
          "downs": 0,
          "version": 1
        },
        "errorType": "DynamoDB:ConditionalCheckFailedException",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
      }
    ]
  }
  ```

  La demande a échoué car l'expression de la condition est fausse : la valeur `version` de la publication dans DynamoDB ne correspond pas à celle spécifiée dans `expectedValue` les arguments. La valeur actuelle de l'objet est renvoyée dans le champ `data` de la section `errors` de la réponse GraphQL.
+ Réessayez la demande, mais corrigez `expectedVersion` :

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 1
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Cette fois, la demande aboutit et la valeur supprimée de DynamoDB est renvoyée :

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```
+ Choisissez **Exécuter une requête** à nouveau.
+ L'appel réussit toujours, mais cette fois aucune valeur n'est renvoyée car la publication a déjà été supprimée dans DynamoDB.

```
{
  "data": {
    "deletePost": null
  }
}
```

## Configuration du résolveur allPost (DynamoDB Scan)
<a name="setting-up-the-allpost-resolver-dynamodb-scan"></a>

Jusqu'à présent, l'API est utile uniquement si vous connaissez l’`id` de chaque publication que vous voulez regarder. Ajoutons un nouveau résolveur qui renvoie toutes les publications de la table.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPost` :

  ```
  type Query {
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```
+ Ajouter un nouveau type `PaginationPosts` :

  ```
  type PaginatedPosts {
      posts: [Post!]!
      nextToken: String
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **allPost** qui vient d'être créé, sur le type **Requête**, choisissez ensuite **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan"
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```

  Ce résolveur possède deux arguments facultatifs : `count`, qui spécifie le nombre maximal d'éléments à renvoyer dans un seul appel, et `nextToken`, qui peut être utilisé pour récupérer l'ensemble de résultats suivant (vous indiquerez ultérieurement d'où provient la valeur de `nextToken`).
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **Remarque :** ce modèle de mappage de réponse est différent de tous les autres jusqu'à présent. Le résultat de la requête `allPost` est un `PaginatedPosts`, qui contient une liste de publications et un jeton de pagination. La forme de cet objet est différente de celle renvoyée par le résolveur AWS AppSync DynamoDB : la liste des publications est appelée `items` dans les résultats du résolveur AWS AppSync DynamoDB, mais elle est appelée. `posts` `PaginatedPosts`
+ Choisissez **Enregistrer**.

Pour en savoir plus sur le mappage des demandes `Scan`, consultez la documentation de référence [Scan](aws-appsync-resolver-mapping-template-reference-dynamodb-scan.md).

### Appel de l'API pour analyser toutes les publications
<a name="call-the-api-to-scan-all-posts"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `allPost` requête entrante en une opération DynamoDB`Scan`. Vous pouvez désormais analyser la table pour récupérer toutes les publications.

Avant de pouvoir essayer, vous avez besoin de remplir la table avec certaines données, car vous avez supprimé tout ce que vous aviez utilisé jusque là.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**.

  ```
  mutation addPost {
    post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).

Maintenant, analysons la table, en renvoyant cinq résultats à la fois.
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  query allPost {
    allPost(count: 5) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Les cinq premières publications doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          },
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRkJEdXdUK09hcnovRGhNTGxLTGdMUEFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF6ajFodkhKU1paT1pncTRaUUNBUkNBZ2dHWnJiR1dQWGxkMDB1N0xEdGY4Z2JsbktzRjRua1VCcks3TFJLcjZBTFRMeGFwVGJZMDRqOTdKVFQyYVRwSzdzbVdtNlhWWFVCTnFIOThZTzBWZHVkdDI2RlkxMHRqMDJ2QTlyNWJTUWpTbWh6NE5UclhUMG9KZWJSQ2JJbXBlaDRSVlg0Tis0WTVCN1IwNmJQWWQzOVhsbTlUTjBkZkFYMVErVCthaXZoNE5jMk50RitxVmU3SlJ5WmpzMEFkSGduM3FWd2VrOW5oeFVVd3JlK1loUks5QkRzemdiMDlmZmFPVXpzaFZ4cVJRbC93RURlOTcrRmVJdXZNby9NZ1F6dUdNbFRyalpNR3FuYzZBRnhwa0VlZTFtR0FwVDFISElUZlluakptYklmMGUzUmcxbVlnVHVSbDh4S0trNmR0QVoraEhLVDhuNUI3VnF4bHRtSnlNUXBrZGl6KzkyL3VzNDl4OWhrMnVxSW01ZFFwMjRLNnF0dm9ZK1BpdERuQTc5djhzb0grVytYT3VuQ2NVVDY4TVZ1Wk5KYkRuSEFSSEVlaTlVNVBTelU5RGZ6d2pPdmhqWDNJMWhwdWUrWi83MDVHVjlPQUxSTGlwZWZPeTFOZFhwZTdHRDZnQW00bUJUK2c1eC9Ec3ZDbWVnSDFDVXRTdHVuU1ZFa2JpZytQRC9oMUwyRTNqSHhVQldaa28yU256WUc0cG0vV1RSWkFVZHZuQT09In0="
      }
    }
  }
  ```

Vous avez cinq résultats et un `nextToken` que vous pouvez utiliser pour obtenir l'ensemble de résultats suivant.
+ Mettez à jour la requête `allPost` pour qu'elle inclue l'élément `nextToken` issu de l'ensemble de résultats précédent :

  ```
  query allPost {
    allPost(
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0="
    ) {
      posts {
        id
        author
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Les quatre publications restantes doivent apparaître dans le volet des résultats à droite du volet de requête. Il n'y a pas d'élément `nextToken` dans cet ensemble de résultats, car vous avez parcouru les neuf publications, et il n'en reste aucune. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## Configuration du résolveur d' allPostsByauteur (requête DynamoDB)
<a name="setting-up-the-allpostsbyauthor-resolver-ddb-query"></a>

En plus de rechercher dans DynamoDB toutes les publications, vous pouvez également interroger DynamoDB pour récupérer les publications créées par un auteur spécifique. La table DynamoDB que vous avez créée précédemment possède déjà `GlobalSecondaryIndex` un `author-index` appel que vous pouvez utiliser avec une opération DynamoDB pour récupérer toutes les `Query` publications créées par un auteur spécifique.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPostsByAuthor` :

  ```
  type Query {
      allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```

   **Remarque :** ceci utilise le même type `PaginatedPosts` que nous avons utilisé avec la requête `allPost`.
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, recherchez le champ **allPostsByAuteur** nouvellement créé dans le type de **requête**, puis choisissez **Joindre**.
+ Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Query",
      "index" : "author-index",
      "query" : {
        "expression": "author = :author",
          "expressionValues" : {
            ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": "${context.arguments.nextToken}"
      #end
  }
  ```

  Comme le résolveur `allPost`, ce résolveur possède deux arguments facultatifs : `count`, qui spécifie le nombre maximal d'éléments à renvoyer dans un seul appel et `nextToken`, qui peut être utilisé pour récupérer l'ensemble de résultats suivant (la valeur de `nextToken` peut être obtenue à partir d'un appel précédent).
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **Remarque :** il s'agit du même modèle de mappage de réponse que vous avez utilisé dans le résolveur `allPost`.
+ Choisissez **Enregistrer**.

Pour en savoir plus sur le mappage des demandes `Query`, consultez la documentation de référence [Requête](aws-appsync-resolver-mapping-template-reference-dynamodb-query.md).

### Appel de l'API pour interroger toutes les publications d'un auteur
<a name="call-the-api-to-query-all-posts-by-an-author"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `allPostsByAuthor` mutation entrante en une opération `Query` DynamoDB par rapport à l'index. `author-index` Vous pouvez désormais interroger la table pour récupérer toutes les publications d'un auteur spécifique.

Avant cela, toutefois, remplissez la table avec d'autres publications car, à ce stade, chaque publication est du même auteur.
+ Choisissez l'onglet **Requêtes**.
+ Collez la mutation suivante dans le volet **Requêtes**.

  ```
  mutation addPost {
    post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title }
    post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title }
    post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).

Maintenant, interrogez la table et renvoyez toutes les publications créées par `Nadia`.
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  query allPostsByAuthor {
    allPostsByAuthor(author: "Nadia") {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Toutes les publications créées par `Nadia` doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you know...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

La pagination fonctionne pour `Query` tout comme elle le fait pour `Scan`. Par exemple, examinons toutes les publications créées par `AUTHORNAME`, et prenons-en cinq à la fois.
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Toutes les publications créées par `AUTHORNAME` doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
      }
    }
  }
  ```
+ Mettez à jour l'argument `nextToken` avec la valeur renvoyée par la requête précédente :

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Les publications créées par `AUTHORNAME` doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          },
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## Utilisation des ensembles
<a name="using-sets"></a>

Jusqu'à présent, le `Post` type était un key/value objet plat. Vous pouvez également modéliser des objets complexes avec le résolveur AWS AppSyncDynamo de base de données, tels que des ensembles, des listes et des cartes.

Mettons à jour notre type `Post` pour inclure des balises. Une publication peut comporter 0 balises ou plus, qui sont stockées dans DynamoDB sous forme de jeu de chaînes. Vous allez également configurer certaines mutations pour ajouter et supprimer des balises, et une nouvelle requête pour rechercher des publications avec une balise spécifique.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, modifiez le type `Post` pour ajouter un nouveau champ `tags` :

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
  }
  ```
+ Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPostsByTag` :

  ```
  type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID): Post
  }
  ```
+ Dans le volet **Schéma**, modifiez le type `Mutation` pour ajouter de nouvelles mutations `addTag` et `removeTag` :

  ```
  type Mutation {
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, recherchez le champ **allPostsByTag** nouvellement créé dans le type de **requête**, puis choisissez **Joindre**.
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan",
      "filter": {
        "expression": "contains (tags, :tag)",
          "expressionValues": {
            ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **addTag** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD tags :tags, version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **removeTag** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "DELETE tags :tags ADD version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.

### Appel de l'API pour utiliser des balises
<a name="call-the-api-to-work-with-tags"></a>

Maintenant que vous avez configuré les résolveurs, vous savez comment AWS AppSync traduire les requêtes entrantes et `addTag` les `removeTag` `allPostsByTag` requêtes en `UpdateItem` DynamoDB `Scan` et en opérations.

Pour tester cela, sélectionnons l'une des publications que nous avons créées précédemment. Par exemple, utilisons un article créé par `Nadia`.
+ Choisissez l'onglet **Requêtes**.
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "Nadia"
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Toutes les publications de Nadia doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you known...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```
+ Utilisons celui avec le titre `"The cutest dog in the world"`. Notez son `id` car vous l'utiliserez ultérieurement.

Essayons maintenant d'ajouter une balise `dog`.
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation addTag {
    addTag(id:10 tag: "dog") {
      id
      title
      tags
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication est mise à jour avec la nouvelle balise.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

Vous pouvez ajouter encore d'autres balises :
+ Mettez à jour la mutation pour modifier l'argument `tag` en spécifiant `puppy`.

  ```
  mutation addTag {
    addTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication est mise à jour avec la nouvelle balise.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog",
          "puppy"
        ]
      }
    }
  }
  ```

Vous pouvez également supprimer des balises :
+ Collez la mutation suivante dans le volet **Requêtes**. Vous devrez également mettre à jour l'argument `id` pour qu'il ait la valeur que vous avez notée précédemment.

  ```
  mutation removeTag {
    removeTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ La publication est mise à jour et la balise `puppy` est supprimée.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

Vous pouvez également rechercher toutes les publications qui ont une balise :
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  query allPostsByTag {
    allPostsByTag(tag: "dog") {
      posts {
        id
        title
        tags
      }
      nextToken
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Toutes les publications qui ont la balise `dog` sont renvoyées :

  ```
  {
    "data": {
      "allPostsByTag": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world",
            "tags": [
              "dog",
              "puppy"
            ]
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## Utilisation de listes et de cartes
<a name="using-lists-and-maps"></a>

Outre les ensembles DynamoDB, vous pouvez également utiliser des listes et des cartes DynamoDB pour modéliser des données complexes dans un seul objet.

Ajoutons la possibilité d'ajouter des commentaires aux publications. Cela sera modélisé sous la forme d'une liste d'objets cartographiques sur l'`Post`objet dans DynamoDB.

 **Remarque :** dans une application réelle, vous devez modéliser les commentaires dans leur propre table. Dans le cadre de ce didacticiel, vous devez simplement les ajouter dans la table `Post`.
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Schéma**, ajoutez un nouveau type `Comment` :

  ```
  type Comment {
      author: String!
      comment: String!
  }
  ```
+ Dans le volet **Schéma**, modifiez le type `Post` pour ajouter un nouveau champ `comments` :

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
  }
  ```
+ Dans le volet **Schéma**, modifiez le type `Mutation` pour ajouter une nouvelle mutation `addComment` :

  ```
  type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ Choisissez **Enregistrer**.
+ Dans le volet **Types de données** sur la droite, se trouve le champ **addComment** qui vient d'être créé, sur le type **Mutation**, choisissez ensuite **Joindre**.
+ Dans **Nom de la source de données**, choisissez **PostDynamoDBTable**.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de demande** :

  ```
  {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
      "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
      "expression" : "SET comments = list_append(if_not_exists(comments, :emptyList), :newComment) ADD version :plusOne",
      "expressionValues" : {
        ":emptyList": { "L" : [] },
        ":newComment" : { "L" : [
          { "M": {
            "author": $util.dynamodb.toDynamoDBJson($context.arguments.author),
            "comment": $util.dynamodb.toDynamoDBJson($context.arguments.comment)
            }
          }
        ] },
        ":plusOne" : $util.dynamodb.toDynamoDBJson(1)
      }
    }
  }
  ```

  Cette expression de mise à jour ajoute une liste contenant notre nouveau commentaire à la liste `comments` existante. Si la liste n'existe pas encore, elle sera créée.
+ Collez ce qui suit dans la section **Configurer le modèle de mappage de réponse** :

  ```
  $utils.toJson($context.result)
  ```
+ Choisissez **Enregistrer**.

### Appel de l'API pour ajouter un commentaire
<a name="call-the-api-to-add-a-comment"></a>

Maintenant que vous avez configuré les résolveurs, vous savez comment AWS AppSync traduire les `addComment` demandes entrantes en opérations DynamoDB`UpdateItem`.

Essayons cela en ajoutant un commentaire à la même publication à laquelle vous avez ajouté les balises.
+ Choisissez l'onglet **Requêtes**.
+ Collez la requête suivante dans le volet **Requêtes** :

  ```
  mutation addComment {
    addComment(
      id:10
      author: "Steve"
      comment: "Such a cute dog."
    ) {
      id
      comments {
        author
        comment
      }
    }
  }
  ```
+ Choisissez **Exécuter une requête** (le bouton de lecture orange).
+ Toutes les publications de Nadia doivent apparaître dans le volet des résultats à droite du volet de requête. Il doit ressembler à l’exemple ci-dessous.

  ```
  {
    "data": {
      "addComment": {
        "id": "10",
        "comments": [
          {
            "author": "Steve",
            "comment": "Such a cute dog."
          }
        ]
      }
    }
  }
  ```

Si vous exécutez la demande plusieurs fois, plusieurs commentaires seront ajoutés à la liste.

## Conclusion
<a name="conclusion"></a>

Dans ce didacticiel, vous avez créé une API qui nous permet de manipuler des objets Post dans DynamoDB à l'aide de AWS AppSync GraphQL. Pour plus d'informations, consultez la [Référence du modèle de mappage des résolveurs](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference).

Pour nettoyer, vous pouvez supprimer l'API AppSync GraphQL de la console.

Pour supprimer la table DynamoDB et le rôle IAM que vous avez créés pour ce didacticiel, vous pouvez exécuter les opérations suivantes pour supprimer la pile, ou accéder à `AWSAppSyncTutorialForAmazonDynamoDB` la console et supprimer CloudFormation la pile :

```
aws cloudformation delete-stack \
    --stack-name AWSAppSyncTutorialForAmazonDynamoDB
```

# Utilisation de AWS Lambda résolveurs dans AWS AppSync
<a name="tutorial-lambda-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Vous pouvez utiliser AWS Lambda with AWS AppSync pour résoudre n'importe quel champ GraphQL. Par exemple, une requête GraphQL peut envoyer un appel à une instance Amazon Relational Database Service (Amazon RDS), et une mutation GraphQL peut écrire dans un flux Amazon Kinesis. Dans cette section, nous allons vous montrer comment écrire une fonction Lambda qui exécute une logique métier basée sur l'invocation d'une opération de terrain GraphQL.

## Création d’une fonction Lambda
<a name="create-a-lam-function"></a>

L'exemple suivant montre une fonction Lambda écrite `Node.js` qui effectue différentes opérations sur des articles de blog dans le cadre d'une application de publication de blog.

```
exports.handler = (event, context, callback) => {
    console.log("Received event {}", JSON.stringify(event, 3));
    var posts = {
         "1": {"id": "1", "title": "First book", "author": "Author1", "url": "https://amazon.com/", "content": "SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1", "ups": "100", "downs": "10"},
         "2": {"id": "2", "title": "Second book", "author": "Author2", "url": "https://amazon.com", "content": "SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT", "ups": "100", "downs": "10"},
         "3": {"id": "3", "title": "Third book", "author": "Author3", "url": null, "content": null, "ups": null, "downs": null },
         "4": {"id": "4", "title": "Fourth book", "author": "Author4", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4", "ups": "1000", "downs": "0"},
         "5": {"id": "5", "title": "Fifth book", "author": "Author5", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT", "ups": "50", "downs": "0"} };

    var relatedPosts = {
        "1": [posts['4']],
        "2": [posts['3'], posts['5']],
        "3": [posts['2'], posts['1']],
        "4": [posts['2'], posts['1']],
        "5": []
    };

    console.log("Got an Invoke Request.");
    switch(event.field) {
        case "getPost":
            var id = event.arguments.id;
            callback(null, posts[id]);
            break;
        case "allPosts":
            var values = [];
            for(var d in posts){
                values.push(posts[d]);
            }
            callback(null, values);
            break;
        case "addPost":
            // return the arguments back
            callback(null, event.arguments);
            break;
        case "addPostErrorWithData":
            var id = event.arguments.id;
            var result = posts[id];
            // attached additional error information to the post
            result.errorMessage = 'Error with the mutation, data has changed';
            result.errorType = 'MUTATION_ERROR';
            callback(null, result);
            break;
        case "relatedPosts":
            var id = event.source.id;
            callback(null, relatedPosts[id]);
            break;
        default:
            callback("Unknown field, unable to resolve" + event.field, null);
            break;
    }
};
```

Cette fonction Lambda récupère une publication par identifiant, ajoute une publication, récupère une liste de publications et récupère les publications associées à une publication donnée.

 **Remarque :** La fonction Lambda utilise l'`switch`instruction on `event.field` pour déterminer le champ en cours de résolution.

Créez cette fonction Lambda à l'aide de la console de AWS gestion ou d'une AWS CloudFormation pile. Pour créer la fonction à partir d'une CloudFormation pile, vous pouvez utiliser la commande suivante AWS Command Line Interface (AWS CLI) :

```
aws cloudformation create-stack --stack-name AppSyncLambdaExample \
--template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/lambda/LambdaCFTemplate.yaml \
--capabilities CAPABILITY_NAMED_IAM
```

Vous pouvez également lancer le CloudFormation stack dans la AWS région ouest des États-Unis (Oregon) sur votre AWS compte à partir d'ici :

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/lambda/LambdaCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/lambda/LambdaCFTemplate.yaml)

## Configuration d'une source de données pour Lambda
<a name="configure-data-source-for-lamlong"></a>

**Après avoir créé la fonction Lambda, accédez à votre API GraphQL dans la AWS AppSync console, puis choisissez l'onglet Sources de données.**

Choisissez **Créer une source de données**, entrez un **nom de source de données** convivial (par exemple,**Lambda**), puis pour **Type de source de données**, choisissez **AWS Lambda fonction**. Pour **Région**, choisissez la même région que votre fonction. (Si vous avez créé la fonction à partir de la CloudFormation pile fournie, elle se trouve probablement dans **US-WEST-2**.) Pour **Function ARN**, choisissez le Amazon Resource Name (ARN) de votre fonction Lambda.

Après avoir choisi votre fonction Lambda, vous pouvez soit créer un nouveau rôle Gestion des identités et des accès AWS (IAM) (pour lequel les autorisations appropriées sont AWS AppSync attribuées), soit choisir un rôle existant doté de la politique intégrée suivante :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": "arn:aws:lambda:us-east-1:111122223333:function:LAMBDA_FUNCTION"
        }
    ]
}
```

------

Vous devez également établir une relation de confiance avec AWS AppSync le rôle IAM comme suit :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

## Création d'un schéma GraphQL
<a name="creating-a-graphql-schema"></a>

Maintenant que la source de données est connectée à votre fonction Lambda, créez un schéma GraphQL.

Dans l'éditeur de schéma de la AWS AppSync console, assurez-vous que votre schéma correspond au schéma suivant :

```
schema {
    query: Query
    mutation: Mutation
}

type Query {
    getPost(id:ID!): Post
    allPosts: [Post]
}

type Mutation {
    addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!
}

type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    relatedPosts: [Post]
}
```

## Configuration des résolveurs
<a name="configuring-resolvers"></a>

Maintenant que vous avez enregistré une source de données Lambda et un schéma GraphQL valide, vous pouvez connecter vos champs GraphQL à votre source de données Lambda à l'aide de résolveurs.

Pour créer un résolveur, vous aurez besoin de modèles de mappage. Pour en savoir plus sur les modèles de mappage, consultez[Resolver Mapping Template Overview](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview).

Pour plus d'informations sur les modèles de mappage Lambda, consultez. [Resolver mapping template reference for Lambda](resolver-mapping-template-reference-lambda.md#aws-appsync-resolver-mapping-template-reference-lambda)

Au cours de cette étape, vous associez un résolveur à la fonction Lambda pour les champs suivants `getPost(id:ID!): Post` :`allPosts: [Post]`,`addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!`, et. `Post.relatedPosts: [Post]`

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`getPost(id:ID!): Post`.

Ensuite, dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**).

Ensuite, choisissez votre source de données Lambda. Dans la section **modèle de mappage de demande**, choisissez **Invoquer et transférer des arguments**.

Modifiez l'objet `payload` pour ajouter le nom du champ. Votre modèle doit ressembler au suivant :

```
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "getPost",
        "arguments":  $utils.toJson($context.arguments)
    }
}
```

Dans la section **modèle de mappage de réponse** section, choisissez **Renvoyer le résultat de Lambda**.

Dans ce cas, utilisez le modèle de base en l'état. Il devrait se présenter comme suit :

```
$utils.toJson($context.result)
```

Choisissez **Enregistrer**. Vous avez joint votre première résolveur avec succès. Répétez cette opération pour les autres champs comme suit :

Pour le modèle de mappage de demande `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!` :

```
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "addPost",
        "arguments":  $utils.toJson($context.arguments)
    }
}
```

Pour le modèle de mappage de réponse `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!` :

```
$utils.toJson($context.result)
```

Pour le modèle de mappage de demande `allPosts: [Post]` :

```
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "allPosts"
    }
}
```

Pour le modèle de mappage de réponse `allPosts: [Post]` :

```
$utils.toJson($context.result)
```

Pour le modèle de mappage de demande `Post.relatedPosts: [Post]` :

```
{
    "version": "2017-02-28",
    "operation": "Invoke",
    "payload": {
        "field": "relatedPosts",
        "source":  $utils.toJson($context.source)
    }
}
```

Pour le modèle de mappage de réponse `Post.relatedPosts: [Post]` :

```
$utils.toJson($context.result)
```

## Testez votre API GraphQL
<a name="testing-your-graphql-api"></a>

Maintenant que votre fonction Lambda est connectée aux résolveurs GraphQL, vous pouvez exécuter des mutations et des requêtes à l'aide de la console ou d'une application cliente.

Sur le côté gauche de la AWS AppSync console, choisissez **Requêtes**, puis collez le code suivant :

### addPost Mutation
<a name="addpost-mutation"></a>

```
mutation addPost {
    addPost(
        id: 6
        author: "Author6"
        title: "Sixth book"
        url: "https://www.amazon.com/"
        content: "This is the book is a tutorial for using GraphQL with AWS AppSync."
    ) {
        id
        author
        title
        content
        url
        ups
        downs
    }
}
```

### getPost Query
<a name="getpost-query"></a>

```
query getPost {
    getPost(id: "2") {
        id
        author
        title
        content
        url
        ups
        downs
    }
}
```

### allPosts Query
<a name="allposts-query"></a>

```
query allPosts {
    allPosts {
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {
            id
            title
        }
    }
}
```

## Erreurs de renvoi
<a name="returning-errors"></a>

Toute résolution de champ donnée peut entraîner une erreur. Avec AWS AppSync, vous pouvez générer des erreurs provenant des sources suivantes :
+ Modèle de mappage de demande ou de réponse
+ Fonction Lambda

### À partir du modèle de mappage
<a name="from-the-mapping-template"></a>

Pour signaler des erreurs intentionnelles, vous pouvez utiliser la méthode d'`$utils.error`assistance du modèle Velocity Template Language (VTL). Elle prend comme un argument un `errorMessage`, un `errorType` et une valeur `data` facultative. L'objet `data` est utile pour renvoyer des données supplémentaires au client, lorsqu'une erreur a été déclenchée. L'objet `data` sera ajouté à `errors` dans la réponse GraphQL finale.

L'exemple suivant montre comment utiliser le modèle de mappage de réponse dans le `Post.relatedPosts: [Post]` :

```
$utils.error("Failed to fetch relatedPosts", "LambdaFailure", $context.result)
```

Cela génère une réponse GraphQL similaire à ce qui suit :

```
{
    "data": {
        "allPosts": [
            {
                "id": "2",
                "title": "Second book",
                "relatedPosts": null
            },
            ...
        ]
    },
    "errors": [
        {
            "path": [
                "allPosts",
                0,
                "relatedPosts"
            ],
            "errorType": "LambdaFailure",
            "locations": [
                {
                    "line": 5,
                    "column": 5
                }
            ],
            "message": "Failed to fetch relatedPosts",
            "data": [
                {
                  "id": "2",
                  "title": "Second book"
                },
                {
                  "id": "1",
                  "title": "First book"
                }
            ]
        }
    ]
}
```

où `allPosts[0].relatedPosts` est *null* du fait de l'erreur et `errorMessage`, `errorType` et `data` sont présents dans l'objet `data.errors[0]`.

### À partir de la fonction Lambda
<a name="from-the-lam-function"></a>

AWS AppSync comprend également les erreurs générées par la fonction Lambda. Le modèle de programmation Lambda vous permet de signaler les erreurs *gérées*. Si la fonction Lambda génère une erreur, elle AWS AppSync ne parvient pas à résoudre le champ actuel. Seul le message d'erreur renvoyé par Lambda est défini dans la réponse. Actuellement, vous ne pouvez pas renvoyer de données superflues au client en déclenchant une erreur à partir de la fonction Lambda.

 **Remarque** : Si votre fonction Lambda génère une erreur *non gérée*, AWS AppSync utilise le message d'erreur défini par Lambda.

La fonction Lambda suivante génère une erreur :

```
exports.handler = (event, context, callback) => {
    console.log("Received event {}", JSON.stringify(event, 3));
    callback("I fail. Always.");
};
```

Cela renvoie une réponse GraphQL similaire à ce qui suit :

```
{
    "data": {
        "allPosts": [
            {
                "id": "2",
                "title": "Second book",
                "relatedPosts": null
            },
            ...
        ]
    },
    "errors": [
        {
            "path": [
                "allPosts",
                0,
                "relatedPosts"
            ],
            "errorType": "Lambda:Handled",
            "locations": [
                {
                    "line": 5,
                    "column": 5
                }
            ],
            "message": "I fail. Always."
        }
    ]
}
```

## Cas d'utilisation avancé : traitement par lots
<a name="advanced-use-case-batching"></a>

Dans cet exemple, la fonction Lambda possède un `relatedPosts` champ qui renvoie une liste de publications connexes pour une publication donnée. Dans les exemples de requêtes, l'invocation du `allPosts` champ par la fonction Lambda renvoie cinq messages. Comme nous avons précisé que nous voulions également résoudre le problème `relatedPosts` pour chaque message renvoyé, l'opération de `relatedPosts` terrain est invoquée cinq fois.

```
query allPosts {
    allPosts {   // 1 Lambda invocation - yields 5 Posts
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {   // 5 Lambda invocations - each yields 5 posts
            id
            title
        }
    }
}
```

Bien que cela ne semble pas important dans cet exemple spécifique, ce surchargement aggravé peut rapidement saper l'application.

Si vous souhaitez à nouveau extraire `relatedPosts` sur le `Posts` associé renvoyé dans la même requête, le nombre d'appels augmenterait considérablement.

```
query allPosts {
    allPosts {   // 1 Lambda invocation - yields 5 Posts
        id
        author
        title
        content
        url
        ups
        downs
        relatedPosts {   // 5 Lambda invocations - each yield 5 posts = 5 x 5 Posts
            id
            title
            relatedPosts {  // 5 x 5 Lambda invocations - each yield 5 posts = 25 x 5 Posts
                id
                title
                author
            }
        }
    }
}
```

Dans cette requête relativement simple, AWS AppSync invoquerait la fonction Lambda 1 \$1 5 \$1 25 = 31 fois.

Il s'agit d'un défi assez courant, souvent appelé « problème N\$11 » (dans ce cas, N = 5) et qui peut entraîner une augmentation de la latence et des coûts de l'application.

L'une des approches possibles pour résoudre ce problème est de regrouper les demandes de résolveur de champ similaires. Dans cet exemple, au lieu de demander à la fonction Lambda de résoudre une liste de publications connexes pour une publication donnée, elle pourrait résoudre une liste de publications connexes pour un lot de publications donné.

Pour démontrer cela, nous allons passer le résolveur `Post.relatedPosts: [Post]` à un résolveur capable d'effectuer des traitements par lots.

Sur le côté droit de la AWS AppSync console, choisissez le `Post.relatedPosts: [Post]` résolveur existant. Modifiez le modèle de mappage de demande comme suit :

```
{
    "version": "2017-02-28",
    "operation": "BatchInvoke",
    "payload": {
        "field": "relatedPosts",
        "source":  $utils.toJson($context.source)
    }
}
```

Seul le champ `operation` est passé de `Invoke` à `BatchInvoke`. Le champ de charge utile devient désormais un tableau de tout ce qui est spécifié dans le modèle. Dans cet exemple, la fonction Lambda reçoit les informations suivantes en entrée :

```
[
    {
        "field": "relatedPosts",
        "source": {
            "id": 1
        }
    },
    {
        "field": "relatedPosts",
        "source": {
            "id": 2
        }
    },
    ...
]
```

Lorsqu'elle `BatchInvoke` est spécifiée dans le modèle de mappage des demandes, la fonction Lambda reçoit une liste de demandes et renvoie une liste de résultats.

Plus précisément, la liste des résultats doit correspondre à la taille et à l'ordre des entrées de charge utile de la demande afin de AWS AppSync pouvoir correspondre aux résultats en conséquence.

Dans cet exemple de traitement par lots, la fonction Lambda renvoie un lot de résultats comme suit :

```
[
    [{"id":"2","title":"Second book"}, {"id":"3","title":"Third book"}],   // relatedPosts for id=1
    [{"id":"3","title":"Third book"}]                                                             // relatedPosts for id=2
]
```

La fonction Lambda suivante dans Node.js illustre cette fonctionnalité de traitement par lots pour le `Post.relatedPosts` champ comme suit :

```
exports.handler = (event, context, callback) => {
    console.log("Received event {}", JSON.stringify(event, 3));
    var posts = {
         "1": {"id": "1", "title": "First book", "author": "Author1", "url": "https://amazon.com/", "content": "SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1", "ups": "100", "downs": "10"},
         "2": {"id": "2", "title": "Second book", "author": "Author2", "url": "https://amazon.com", "content": "SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT", "ups": "100", "downs": "10"},
         "3": {"id": "3", "title": "Third book", "author": "Author3", "url": null, "content": null, "ups": null, "downs": null },
         "4": {"id": "4", "title": "Fourth book", "author": "Author4", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4", "ups": "1000", "downs": "0"},
         "5": {"id": "5", "title": "Fifth book", "author": "Author5", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT", "ups": "50", "downs": "0"} };

    var relatedPosts = {
        "1": [posts['4']],
        "2": [posts['3'], posts['5']],
        "3": [posts['2'], posts['1']],
        "4": [posts['2'], posts['1']],
        "5": []
    };

    console.log("Got a BatchInvoke Request. The payload has %d items to resolve.", event.length);
    // event is now an array
    var field = event[0].field;
    switch(field) {
        case "relatedPosts":
            var results = [];
            // the response MUST contain the same number
            // of entries as the payload array
            for (var i=0; i< event.length; i++) {
                console.log("post {}", JSON.stringify(event[i].source));
                results.push(relatedPosts[event[i].source.id]);
            }
            console.log("results {}", JSON.stringify(results));
            callback(null, results);
            break;
        default:
            callback("Unknown field, unable to resolve" + field, null);
            break;
    }
};
```

### Renvoi d'erreurs individuelles
<a name="returning-individual-errors"></a>

Les exemples précédents montrent qu'il est possible de renvoyer une seule erreur à partir de la fonction Lambda ou de générer une erreur à partir des modèles de mappage. Pour les appels par lots, le fait de générer une erreur à partir de la fonction Lambda indique qu'un lot entier a échoué. Cela peut être acceptable pour des scénarios spécifiques dans lesquels une erreur irrécupérable se produit, telle qu'un échec de connexion à un magasin de données. Toutefois, dans les cas où certains éléments du lot réussissent et d'autres échouent, il est possible de renvoyer à la fois des erreurs et des données valides. Étant donné que la réponse par lots AWS AppSync nécessite de répertorier les éléments correspondant à la taille d'origine du lot, vous devez définir une structure de données capable de différencier les données valides d'une erreur.

*Par exemple, si la fonction Lambda est censée renvoyer un lot de publications connexes, vous pouvez choisir de renvoyer une liste d'`Response`objets dans laquelle chaque objet comporte des *données* facultatives, des champs ErrorMessage et *ErrorType*.* Si le champ *errorMessage* est présent, cela signifie qu'une erreur s'est produite.

Le code suivant montre comment mettre à jour la fonction Lambda :

```
exports.handler = (event, context, callback) => {
    console.log("Received event {}", JSON.stringify(event, 3));
    var posts = {
         "1": {"id": "1", "title": "First book", "author": "Author1", "url": "https://amazon.com/", "content": "SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1", "ups": "100", "downs": "10"},
         "2": {"id": "2", "title": "Second book", "author": "Author2", "url": "https://amazon.com", "content": "SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT", "ups": "100", "downs": "10"},
         "3": {"id": "3", "title": "Third book", "author": "Author3", "url": null, "content": null, "ups": null, "downs": null },
         "4": {"id": "4", "title": "Fourth book", "author": "Author4", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4", "ups": "1000", "downs": "0"},
         "5": {"id": "5", "title": "Fifth book", "author": "Author5", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT", "ups": "50", "downs": "0"} };

    var relatedPosts = {
        "1": [posts['4']],
        "2": [posts['3'], posts['5']],
        "3": [posts['2'], posts['1']],
        "4": [posts['2'], posts['1']],
        "5": []
    };

    console.log("Got a BatchInvoke Request. The payload has %d items to resolve.", event.length);
    // event is now an array
    var field = event[0].field;
    switch(field) {
        case "relatedPosts":
            var results = [];
            results.push({ 'data': relatedPosts['1'] });
            results.push({ 'data': relatedPosts['2'] });
            results.push({ 'data': null, 'errorMessage': 'Error Happened', 'errorType': 'ERROR' });
            results.push(null);
            results.push({ 'data': relatedPosts['3'], 'errorMessage': 'Error Happened with last result', 'errorType': 'ERROR' });
            callback(null, results);
            break;
        default:
            callback("Unknown field, unable to resolve" + field, null);
            break;
    }
};
```

Dans cet exemple, le modèle de mappage des réponses suivant analyse chaque élément de la fonction Lambda et signale les erreurs éventuelles :

```
#if( $context.result && $context.result.errorMessage )
    $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data)
#else
    $utils.toJson($context.result.data)
#end
```

Cet exemple renvoie une réponse GraphQL similaire à ce qui suit :

```
{
  "data": {
    "allPosts": [
      {
        "id": "1",
        "relatedPostsPartialErrors": [
          {
            "id": "4",
            "title": "Fourth book"
          }
        ]
      },
      {
        "id": "2",
        "relatedPostsPartialErrors": [
          {
            "id": "3",
            "title": "Third book"
          },
          {
            "id": "5",
            "title": "Fifth book"
          }
        ]
      },
      {
        "id": "3",
        "relatedPostsPartialErrors": null
      },
      {
        "id": "4",
        "relatedPostsPartialErrors": null
      },
      {
        "id": "5",
        "relatedPostsPartialErrors": null
      }
    ]
  },
  "errors": [
    {
      "path": [
        "allPosts",
        2,
        "relatedPostsPartialErrors"
      ],
      "errorType": "ERROR",
      "locations": [
        {
          "line": 4,
          "column": 9
        }
      ],
      "message": "Error Happened"
    },
    {
      "path": [
        "allPosts",
        4,
        "relatedPostsPartialErrors"
      ],
      "data": [
        {
          "id": "2",
          "title": "Second book"
        },
        {
          "id": "1",
          "title": "First book"
        }
      ],
      "errorType": "ERROR",
      "locations": [
        {
          "line": 4,
          "column": 9
        }
      ],
      "message": "Error Happened with last result"
    }
  ]
}
```

### Configuration de la taille de lot maximale
<a name="configure-max-batch-size"></a>

Par défaut, lors de l'utilisation`BatchInvoke`, AWS AppSync envoie des requêtes à votre fonction Lambda par lots de cinq éléments maximum. Vous pouvez configurer la taille de lot maximale de vos résolveurs Lambda.

Pour configurer la taille de lot maximale sur un résolveur, utilisez la commande suivante dans le AWS Command Line Interface ()AWS CLI :

```
$ aws appsync create-resolver --api-id <api-id> --type-name Query --field-name relatedPosts \
 --request-mapping-template "<template>" --response-mapping-template "<template>" --data-source-name "<lambda-datasource>" \ 
 --max-batch-size X
```

**Note**  
Lorsque vous fournissez un modèle de mappage de demandes, vous devez utiliser l'`BatchInvoke`opération pour utiliser le traitement par lots.

Vous pouvez également utiliser la commande suivante pour activer et configurer le traitement par lots sur les résolveurs Lambda directs :

```
$ aws appsync create-resolver --api-id <api-id> --type-name Query --field-name relatedPosts \
 --data-source-name "<lambda-datasource>" \ 
 --max-batch-size X
```

### Configuration de la taille de lot maximale avec des modèles VTL
<a name="configure-max-batch-size-vtl"></a>

Pour les résolveurs Lambda dotés de modèles VTL intégrés à la demande, la taille de lot maximale n'aura aucun effet à moins qu'ils ne l'aient directement spécifiée en tant qu'opération dans VTL. `BatchInvoke` De même, si vous effectuez une mutation de haut niveau, le traitement par lots n'est pas effectué pour les mutations car la spécification GraphQL exige que les mutations parallèles soient exécutées de manière séquentielle.

Prenons par exemple les mutations suivantes :

```
type Mutation {
    putItem(input: Item): Item
    putItems(inputs: [Item]): [Item]
}
```

En utilisant la première mutation, nous pouvons créer 10 `Items` comme indiqué dans l'extrait ci-dessous :

```
mutation MyMutation {
    v1: putItem($someItem1) {
        id,
        name
    }
    v2: putItem($someItem2) {
        id,
        name
    }
    v3: putItem($someItem3) {
        id,
        name
    } 
    v4: putItem($someItem4) {
        id,
        name
    }
    v5: putItem($someItem5) {
        id,
        name
    }
    v6: putItem($someItem6) {
        id,
        name
    } 
    v7: putItem($someItem7) {
        id,
        name
    }
    v8: putItem($someItem8) {
        id,
        name
    }
    v9: putItem($someItem9) {
        id,
        name
    }
    v10: putItem($someItem10) {
        id,
        name
    }
}
```

Dans cet exemple, le ne `Items` sera pas regroupé par lots de 10, même si la taille de lot maximale est définie sur 10 dans le résolveur Lambda. Ils s'exécuteront plutôt de manière séquentielle conformément à la spécification GraphQL.

Pour effectuer une véritable mutation par lots, vous pouvez suivre l'exemple ci-dessous en utilisant la deuxième mutation :

```
mutation MyMutation {
    putItems([$someItem1, $someItem2, $someItem3,$someItem4, $someItem5, $someItem6, 
    $someItem7, $someItem8, $someItem9, $someItem10]) {
    id,
    name
    }
}
```

Pour plus d'informations sur l'utilisation du traitement par lots avec les résolveurs Lambda directs, consultez. [Résolveurs Lambda directs](resolver-mapping-template-reference-lambda.md#direct-lambda-resolvers)

# Utilisation des résolveurs Amazon OpenSearch Service dans AWS AppSync
<a name="tutorial-elasticsearch-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync prend en charge l'utilisation d'Amazon OpenSearch Service à partir de domaines que vous avez provisionnés dans votre propre AWS compte, à condition qu'ils n'existent pas au sein d'un VPC. Une fois que vos domaines sont mis en service, vous pouvez vous y connecter à l'aide d'une source de données, puis vous pouvez configurer un résolveur dans le schéma afin d'effectuer des opérations GraphQL telles que des requêtes, des mutations et des abonnements. Ce didacticiel vous présente certains exemples courants.

Pour plus d'informations, consultez la [référence du modèle de mappage du résolveur pour OpenSearch](resolver-mapping-template-reference-elasticsearch.md#aws-appsync-resolver-mapping-template-reference-elasticsearch).

## Configuration en un clic
<a name="one-click-setup"></a>

Pour configurer automatiquement un point de terminaison GraphQL avec AWS AppSync Amazon OpenSearch Service configuré, vous pouvez utiliser ce AWS CloudFormation modèle :

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/appsynces.yml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/appsynces.yml)

Une fois le AWS CloudFormation déploiement terminé, vous pouvez passer directement à l'[exécution de requêtes et de mutations GraphQL](#tutorial-elasticsearch-resolvers-perform-queries-mutations).

## Création d'un nouveau domaine OpenSearch de service
<a name="create-a-new-es-domain"></a>

Pour commencer à utiliser ce didacticiel, vous avez besoin d'un domaine OpenSearch de service existant. Si vous n'en avez pas, vous pouvez utiliser l'exemple suivant. Notez que la création d'un domaine de OpenSearch service peut prendre jusqu'à 15 minutes avant que vous puissiez passer à son intégration à une source de AWS AppSync données.

```
aws cloudformation create-stack --stack-name AppSyncOpenSearch \
--template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml \
--parameters ParameterKey=OSDomainName,ParameterValue=ddtestdomain ParameterKey=Tier,ParameterValue=development \
--capabilities CAPABILITY_NAMED_IAM
```

Vous pouvez lancer le AWS CloudFormation stack suivant dans la région USA West 2 (Oregon) sur votre AWS compte :

 [https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/elasticsearch/ESResolverCFTemplate.yaml)

## Configuration de la source de données pour le OpenSearch service
<a name="configure-data-source-for-es"></a>

Une fois le domaine de OpenSearch service créé, accédez à votre API AWS AppSync GraphQL et choisissez l'onglet **Sources de données**. Choisissez **Nouveau** et entrez un nom convivial pour la source de données, tel que « oss ». Choisissez ensuite le ** OpenSearch domaine Amazon** pour le **type de source de données**, choisissez la région appropriée et vous devriez voir votre domaine OpenSearch de service répertorié. Après l'avoir sélectionné, vous pouvez soit créer un nouveau rôle et AWS AppSync attribuer les autorisations appropriées au rôle, soit choisir un rôle existant, dont la politique intégrée est la suivante :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "Stmt1234234",
            "Effect": "Allow",
            "Action": [
                "es:ESHttpDelete",
                "es:ESHttpHead",
                "es:ESHttpGet",
                "es:ESHttpPost",
                "es:ESHttpPut"
            ],
            "Resource": [
                "arn:aws:es:us-east-1:111122223333:domain/democluster/*"
            ]
        }
    ]
}
```

------

Vous devrez également établir une relation de confiance avec AWS AppSync pour ce rôle :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

En outre, le domaine de OpenSearch service possède sa propre **politique d'accès** que vous pouvez modifier via la console Amazon OpenSearch Service. Vous devrez ajouter une politique similaire à la suivante, avec les actions et les ressources appropriées pour le domaine OpenSearch de service. Notez que le **principal** sera le rôle de source de AppSync données qui, si vous laissez la console le créer, se trouve dans la console IAM.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:role/service-role/APPSYNC_DATASOURCE_ROLE"
            },
            "Action": [
                "es:ESHttpDelete",
                "es:ESHttpHead",
                "es:ESHttpGet",
                "es:ESHttpPost",
                "es:ESHttpPut"
            ],
            "Resource": "arn:aws:es:us-east-1:111122223333:domain/DOMAIN_NAME/*"
        }
    ]
}
```

------

## Connexion d'un résolveur
<a name="connecting-a-resolver"></a>

Maintenant que la source de données est connectée à votre domaine de OpenSearch service, vous pouvez la connecter à votre schéma GraphQL à l'aide d'un résolveur, comme illustré dans l'exemple suivant :

```
 schema {
   query: Query
   mutation: Mutation
 }

 type Query {
   getPost(id: ID!): Post
   allPosts: [Post]
 }

 type Mutation {
   addPost(id: ID!, author: String, title: String, url: String, ups: Int, downs: Int, content: String): AWSJSON
 }

type Post {
  id: ID!
  author: String
  title: String
  url: String
  ups: Int
  downs: Int
  content: String
}
...
```

Notez qu'il y a un type `Post` défini par l'utilisateur avec un champ `id`. Dans les exemples suivants, nous supposons qu'il existe un processus (qui peut être automatisé) permettant de placer ce type dans votre domaine de OpenSearch service, qui correspondrait à une racine de chemin de`/post/_doc`, où se `post` trouve l'index. À partir de ce chemin racine, vous pouvez effectuer des recherches de documents individuels, des recherches par `/id/post*` caractères génériques ou des recherches multidocuments avec un chemin de. `/post/_search` Par exemple, si un autre type est appelé`User`, vous pouvez indexer les documents sous un nouvel index appelé`user`, puis effectuer des recherches avec un **chemin** de`/user/_search`. 

Dans l'éditeur de schéma de la AWS AppSync console, modifiez le `Posts` schéma précédent pour inclure une `searchPosts` requête :

```
type Query {
  getPost(id: ID!): Post
  allPosts: [Post]
  searchPosts: [Post]
}
```

Enregistrez le schéma. Sur le côté droit, pour `searchPosts`, choisissez **Attach resolver (Attacher un résolveur)**. Dans le **menu Action**, choisissez **Update runtime**, puis **Unit Resolver (VTL uniquement**). Choisissez ensuite votre source OpenSearch de données de service. Sous la section **request mapping template (modèle de mappage de demande)**, sélectionnez dans la liste déroulante les **Query posts (billets de requête)** pour obtenir un modèle de base. Modifiez l'élément `path` en lui donnant la valeur `/post/_search`. Il devrait se présenter comme suit :

```
{
    "version":"2017-02-28",
    "operation":"GET",
    "path":"/post/_search",
    "params":{
        "headers":{},
        "queryString":{},
        "body":{
            "from":0,
            "size":50
        }
    }
}
```

Cela suppose que le schéma précédent contient des documents indexés dans OpenSearch Service sous le `post` champ. Si vous structurez vos données différemment, vous devrez faire la mise à jour correspondante.

Dans la section du **modèle de mappage des réponses**, vous devez spécifier le `_source` filtre approprié si vous souhaitez récupérer les résultats des données d'une requête de OpenSearch service et les traduire vers GraphQL. Utilisez le modèle suivant :

```
[
    #foreach($entry in $context.result.hits.hits)
    #if( $velocityCount > 1 ) , #end
    $utils.toJson($entry.get("_source"))
    #end
]
```

## Modification de vos recherches
<a name="modifying-your-searches"></a>

Le modèle de mappage de demande précédent exécute une requête simple pour tous les enregistrements. Supposons que vous souhaitiez effectuer une recherche en fonction d'un auteur spécifique. Supposons également que vous vouliez que l'auteur soit un argument défini dans votre requête GraphQL. Dans l'éditeur de schéma de la console AWS AppSync, ajoutez une requête `allPostsByAuthor` :

```
type Query {
  getPost(id: ID!): Post
  allPosts: [Post]
  allPostsByAuthor(author: String!): [Post]
  searchPosts: [Post]
}
```

Choisissez maintenant **Attach resolver** et sélectionnez la source de données du OpenSearch service, mais utilisez l'exemple suivant dans le **modèle de mappage des réponses** :

```
{
    "version":"2017-02-28",
    "operation":"GET",
    "path":"/post/_search",
    "params":{
        "headers":{},
        "queryString":{},
        "body":{
            "from":0,
            "size":50,
            "query":{
                "match" :{
                    "author": $util.toJson($context.arguments.author)
                }
            }
        }
    }
}
```

Notez que l'élément `body` est renseigné avec une requête terminologique concernant le champ `author`, qui est transmise à partir du client en tant qu'argument. Vous pouvez, le cas échéant, avoir certaines informations pré-renseignées, telles que le texte standard, ou encore utiliser d'autres [utilitaires](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference).

Si vous utilisez ce résolveur, renseignez la section **response mapping template (modèle de mappage de réponse)** avec les mêmes informations que dans l'exemple précédent.

## Ajouter des données au OpenSearch service
<a name="adding-data-to-es"></a>

Vous souhaiterez peut-être ajouter des données à votre domaine OpenSearch de service à la suite d'une mutation GraphQL. Il s'agit d'un puissant mécanisme de recherche, qui peut également avoir d'autres fonctions. Comme vous pouvez utiliser les abonnements GraphQL pour générer [vos données en temps réel](aws-appsync-real-time-data.md), il s'agit d'un mécanisme permettant d'informer les clients des mises à jour des données de votre OpenSearch domaine de service.

Retournez à la page **Schéma** de la AWS AppSync console et sélectionnez **Attacher un résolveur** pour la `addPost()` mutation. Sélectionnez à nouveau la source de données du OpenSearch service et utilisez le **modèle de mappage des réponses** suivant pour le `Posts` schéma :

```
{
    "version":"2017-02-28",
    "operation":"PUT",
    "path": $util.toJson("/post/_doc/$context.arguments.id"),
    "params":{
        "headers":{},
        "queryString":{},
        "body":{
            "id": $util.toJson($context.arguments.id),
            "author": $util.toJson($context.arguments.author),
            "ups": $util.toJson($context.arguments.ups),
            "downs": $util.toJson($context.arguments.downs),
            "url": $util.toJson($context.arguments.url),
            "content": $util.toJson($context.arguments.content),
            "title": $util.toJson($context.arguments.title)
        }
    }
}
```

Comme auparavant, il s'agit d'un exemple de la façon dont vos données peuvent être structurées. Si vous avez des noms de champs ou des index différents, vous devez mettre à jour les éléments `path` et `body` en conséquence. Cet exemple montre également comment utiliser `$context.arguments` pour renseigner le modèle à partir de vos arguments de mutation GraphQL.

Avant de poursuivre, utilisez le modèle de mappage des réponses suivant, qui renverra le résultat de l'opération de mutation ou les informations d'erreur en sortie :

```
#if($context.error)
    $util.toJson($ctx.error)
#else
    $util.toJson($context.result)
#end
```

## Récupération d'un document unique
<a name="retrieving-a-single-document"></a>

Enfin, si vous souhaitez utiliser la requête `getPost(id:ID)` dans votre schéma pour renvoyer un document individuel, recherchez cette requête dans l'éditeur de schéma de la console AWS AppSync et choisissez **Joindre un résolveur**. Sélectionnez à nouveau la source de données du OpenSearch service et utilisez le modèle de mappage suivant :

```
{
    "version":"2017-02-28",
    "operation":"GET",
    "path": $util.toJson("post/_doc/$context.arguments.id"),
    "params":{
        "headers":{},
        "queryString":{},
        "body":{}
    }
}
```

Étant donné que l'élément `path` ci-dessus utilise l'argument `id` avec un corps vide, c'est un document unique qui est renvoyé. Cependant, vous devez utiliser le modèle de mappage de réponse suivant, car vous souhaitez renvoyer un seul élément et non une liste :

```
$utils.toJson($context.result.get("_source"))
```

## Effectuer des requêtes et des mutations
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations"></a>

Vous devriez maintenant être en mesure d'effectuer des opérations GraphQL sur votre domaine de OpenSearch service. Accédez à l'onglet **Requêtes** de la AWS AppSync console et ajoutez un nouvel enregistrement :

```
mutation addPost {
    addPost (
        id:"12345"
        author: "Fred"
        title: "My first book"
        content: "This will be fun to write!"
        url: "publisher website",
        ups: 100,
        downs:20 
       )
}
```

Vous verrez le résultat de la mutation sur la droite. De même, vous pouvez désormais exécuter une `searchPosts` requête sur votre domaine OpenSearch de service :

```
query searchPosts {
    searchPosts {
        id
        title
        author
        content
    }
}
```

## Bonnes pratiques
<a name="best-practices"></a>
+ OpenSearch Le service doit être destiné à interroger des données, et non en tant que base de données principale. Vous souhaiterez peut-être utiliser le OpenSearch Service conjointement avec Amazon DynamoDB, comme indiqué dans [Combining](tutorial-combining-graphql-resolvers.md#aws-appsync-tutorial-combining-graphql-resolvers) GraphQL Resolvers.
+ Ne donnez accès à votre domaine qu'en autorisant le rôle AWS AppSync de service à accéder au cluster.
+ Vous pouvez commencer la phase de développement de façon modeste, avec le cluster le moins onéreux, puis passer à un plus grand cluster à haute disponibilité (HA) lorsque vous passerez en production.

# Utilisation de résolveurs locaux dans AWS AppSync
<a name="tutorial-local-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync vous permet d'utiliser des sources de données prises en charge (AWS Lambda Amazon DynamoDB ou OpenSearch Amazon Service) pour effectuer diverses opérations. Cependant, dans certains cas, un appel à une source de données prise en charge peut ne pas être nécessaire.

C'est là où le résolveur local se révèle pratique. Au lieu d'appeler une source de données distante, le résolveur local **achemine** simplement le résultat du modèle de mappage de la demande vers le modèle de mappage de la réponse. La résolution du champ ne quitte pas AWS AppSync.

Les résolveurs locaux sont utiles dans plusieurs scénarios. Le scénario le plus répandu consiste à publier des notifications sans déclencher d'appel de source de données. Pour illustrer ce cas d'utilisation, nous allons créer une application de pagination, où les utilisateurs peuvent paginer entre eux. Comme cet exemple met à profit les *Abonnements*, si vous n'êtes pas familiarisé avec les *Abonnements*, vous pouvez suivre le didacticiel [Données en temps réel](aws-appsync-real-time-data.md).

## Création de l'application de pagination
<a name="create-the-paging-application"></a>

Dans notre application de pagination, les clients peuvent s'abonner à une boîte de réception et envoyer des pages à d'autres clients. Chaque page comprend un message. Voici le schéma :

```
schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

type Subscription {
    inbox(to: String!): Page
    @aws_subscribe(mutations: ["page"])
}

type Mutation {
    page(body: String!, to: String!): Page!
}

type Page {
    from: String
    to: String!
    body: String!
    sentAt: String!
}

type Query {
    me: String
}
```

Attachons un résolveur au champ `Mutation.page`. Dans le volet **Schema (Schéma)**, cliquez sur *Attach Resolver (Attacher un résolveur)* en regard de la définition du champ sur le volet droit. Créez une nouvelle source de données de type *None* et nommez-la *PageDataSource*.

Pour le modèle de mappage de la demande, saisissez :

```
{
  "version": "2017-02-28",
  "payload": {
    "body": $util.toJson($context.arguments.body),
    "from": $util.toJson($context.identity.username),
    "to":  $util.toJson($context.arguments.to),
    "sentAt": "$util.time.nowISO8601()"
  }
}
```

Et pour le modèle de mappage de la réponse, sélectionnez la valeur par défaut *Forward the result (Transmettre le résultat)*. Enregistrez votre résolveur. Votre application est désormais prête, commençons à paginer \$1

## Envoi et abonnement aux pages
<a name="send-and-subscribe-to-pages"></a>

Pour que les clients reçoivent des pages, ils doivent d'abord s'abonner à une boîte de réception.

Dans le volet **Requêtes**, exécutons l'abonnement `inbox` :

```
subscription Inbox {
    inbox(to: "Nadia") {
        body
        to
        from
        sentAt
    }
}
```

 *Nadia* recevra les pages chaque fois que la mutation `Mutation.page` est appelée. Appelons la mutation en l'exécutant :

```
mutation Page {
    page(to: "Nadia", body: "Hello, World!") {
        body
        to
        from
        sentAt
    }
}
```

Nous venons juste d'illustrer l'utilisation des résolveurs locaux, en envoyant une page et en la recevant sans quitter AWS AppSync.

# Combinaison de résolveurs GraphQL dans AWS AppSync
<a name="tutorial-combining-graphql-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Les résolveurs et les champs d'un schéma GraphQL possèdent des relations 1:1 avec un haut niveau de flexibilité. Étant donné qu'une source de données est configurée sur un résolveur indépendamment d'un schéma, les types GraphQL peuvent être résolus ou manipulés via différentes sources de données, en les combinant dans un schéma afin de répondre au mieux à vos besoins.

Les exemples de scénarios suivants montrent comment mélanger et associer des sources de données dans votre schéma. Avant de commencer, nous vous recommandons de vous familiariser avec la configuration des sources de données et des résolveurs pour AWS Lambda Amazon DynamoDB OpenSearch et Amazon Service, comme décrit dans les didacticiels précédents.

## Exemple de schéma
<a name="example-schema"></a>

Le schéma suivant est de type `Post` avec 3 `Query` opérations et 3 `Mutation` opérations définies :

```
type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    version: Int!
}

type Query {
    allPost: [Post]
    getPost(id: ID!): Post
    searchPosts: [Post]
}

type Mutation {
    addPost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String
    ): Post
    updatePost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String,
        ups: Int!,
        downs: Int!,
        expectedVersion: Int!
    ): Post
    deletePost(id: ID!): Post
}
```

Dans cet exemple, vous disposez d'un total de 6 résolveurs à joindre. Une solution possible serait de faire en sorte que tous ces éléments proviennent d'une table Amazon DynamoDB, `Posts` appelée, `AllPosts` où exécute un scan `searchPosts` et exécute une requête, comme indiqué dans la référence du modèle de mappage du résolveur [DynamoDB](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb). Cependant, il existe des alternatives pour répondre aux besoins de votre entreprise, comme la résolution de ces requêtes GraphQL à partir de Lambda ou de Service. OpenSearch 

## Modifier les données via les résolveurs
<a name="alter-data-through-resolvers"></a>

Il se peut que vous deviez renvoyer les résultats d'une base de données telle que DynamoDB (ou Amazon Aurora) à des clients dont certains attributs ont été modifiés. Cela peut être dû à la mise en forme des types de données (par exemple, différences d'horodatage entre les clients) ou à des problèmes de rétrocompatibilité. À titre d'illustration, dans l'exemple suivant, une AWS Lambda fonction manipule les votes positifs et négatifs pour les articles de blog en leur attribuant des nombres aléatoires chaque fois que le résolveur GraphQL est invoqué :

```
'use strict';
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

exports.handler = (event, context, callback) => {
    const payload = {
        TableName: 'Posts',
        Limit: 50,
        Select: 'ALL_ATTRIBUTES',
    };

    dynamo.scan(payload, (err, data) => {
        const result = { data: data.Items.map(item =>{
            item.ups = parseInt(Math.random() * (50 - 10) + 10, 10);
            item.downs = parseInt(Math.random() * (20 - 0) + 0, 10);
            return item;
        }) };
        callback(err, result.data);
    });
};
```

Il s'agit d'une fonction Lambda parfaitement valide qui peut être attachée à un champ `AllPosts` dans le schéma GraphQL afin que toute requête renvoyant tous les résultats obtienne des nombres aléatoires pour les pour et/ou les contre.

## DynamoDB et service OpenSearch
<a name="ddb-and-es"></a>

Pour certaines applications, vous pouvez effectuer des mutations ou de simples requêtes de recherche sur DynamoDB et demander à un processus en arrière-plan de transférer des documents vers Service. OpenSearch Vous pouvez ensuite simplement associer le `searchPosts` résolveur à la source de données du OpenSearch service et renvoyer les résultats de recherche (à partir de données provenant de DynamoDB) à l'aide d'une requête GraphQL. Cela peut être extrêmement intéressant lorsque vous ajoutez des opérations de recherche avancée à vos applications, telles que des mots-clés, des recherches de correspondance partielle ou même des recherches géospatiales. Le transfert de données depuis DynamoDB peut être effectué via un processus ETL ou vous pouvez également diffuser des données depuis DynamoDB à l'aide de Lambda. Vous pouvez en lancer un exemple complet en utilisant la AWS CloudFormation pile suivante dans la région USA West 2 (Oregon) sur votre AWS compte :

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/multipledatasource/appsyncesdbstream.yml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/multipledatasource/appsyncesdbstream.yml) 

Le schéma de cet exemple vous permet d'ajouter des publications à l'aide d'un résolveur DynamoDB comme suit :

```
mutation add {
    putPost(author:"Nadia"
        title:"My first post"
        content:"This is some test content"
        url:"https://aws.amazon.com/appsync/"
    ){
        id
        title
    }
}
```

Cela écrit des données dans DynamoDB, qui les diffuse ensuite via Lambda vers OpenSearch Amazon Service, dans lequel vous pouvez rechercher tous les articles dans différents champs. Par exemple, comme les données se trouvent dans Amazon OpenSearch Service, vous pouvez effectuer une recherche dans les champs d'auteur ou de contenu avec du texte libre, même avec des espaces, comme suit :

```
query searchName{
    searchAuthor(name:"   Nadia   "){
        id
        title
        content
    }
}

query searchContent{
    searchContent(text:"test"){
        id
        title
        content
    }
}
```

Les données étant écrites directement dans DynamoDB, vous pouvez toujours effectuer des opérations efficaces de recherche de listes ou d'éléments par rapport à la table contenant `allPosts{...}` les requêtes et. `singlePost{...}` Cette pile utilise l'exemple de code suivant pour les flux DynamoDB :

 **Remarque :** ce code est fourni à titre d'exemple uniquement.

```
var AWS = require('aws-sdk');
var path = require('path');
var stream = require('stream');

var esDomain = {
    endpoint: 'https://opensearch-domain-name.REGION.es.amazonaws.com',
    region: 'REGION',
    index: 'id',
    doctype: 'post'
};

var endpoint = new AWS.Endpoint(esDomain.endpoint)
var creds = new AWS.EnvironmentCredentials('AWS');

function postDocumentToES(doc, context) {
    var req = new AWS.HttpRequest(endpoint);

    req.method = 'POST';
    req.path = '/_bulk';
    req.region = esDomain.region;
    req.body = doc;
    req.headers['presigned-expires'] = false;
    req.headers['Host'] = endpoint.host;

    // Sign the request (Sigv4)
    var signer = new AWS.Signers.V4(req, 'es');
    signer.addAuthorization(creds, new Date());

    // Post document to ES
    var send = new AWS.NodeHttpClient();
    send.handleRequest(req, null, function (httpResp) {
        var body = '';
        httpResp.on('data', function (chunk) {
            body += chunk;
        });
        httpResp.on('end', function (chunk) {
            console.log('Successful', body);
            context.succeed();
        });
    }, function (err) {
        console.log('Error: ' + err);
        context.fail();
    });
}

exports.handler = (event, context, callback) => {
    console.log("event => " + JSON.stringify(event));
    var posts = '';

    for (var i = 0; i < event.Records.length; i++) {
        var eventName = event.Records[i].eventName;
        var actionType = '';
        var image;
        var noDoc = false;
        switch (eventName) {
            case 'INSERT':
                actionType = 'create';
                image = event.Records[i].dynamodb.NewImage;
                break;
            case 'MODIFY':
                actionType = 'update';
                image = event.Records[i].dynamodb.NewImage;
                break;
            case 'REMOVE':
            actionType = 'delete';
                image = event.Records[i].dynamodb.OldImage;
                noDoc = true;
                break;
        }

        if (typeof image !== "undefined") {
            var postData = {};
            for (var key in image) {
                if (image.hasOwnProperty(key)) {
                    if (key === 'postId') {
                        postData['id'] = image[key].S;
                    } else {
                        var val = image[key];
                        if (val.hasOwnProperty('S')) {
                            postData[key] = val.S;
                        } else if (val.hasOwnProperty('N')) {
                            postData[key] = val.N;
                        }
                    }
                }
            }

            var action = {};
            action[actionType] = {};
            action[actionType]._index = 'id';
            action[actionType]._type = 'post';
            action[actionType]._id = postData['id'];
            posts += [
                JSON.stringify(action),
            ].concat(noDoc?[]:[JSON.stringify(postData)]).join('\n') + '\n';
        }
    }
    console.log('posts:',posts);
    postDocumentToES(posts, context);
};
```

Vous pouvez ensuite utiliser des flux DynamoDB pour l'associer à une table DynamoDB avec une clé primaire de, et toute modification apportée à la source `id` de DynamoDB sera répercutée dans votre domaine de service. OpenSearch Pour plus d'informations sur la configuration de ce processus, consultez la [Documentation DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html).

# Utilisation des opérations par lots DynamoDB dans AWS AppSync
<a name="tutorial-dynamodb-batch"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync prend en charge l'utilisation des opérations par lots Amazon DynamoDB sur une ou plusieurs tables d'une même région. Les opérations prises en charge sont `BatchGetItem`, `BatchPutItem` et `BatchDeleteItem`. L'utilisation de ces fonctions dans AWS AppSync vous permet d'exécuter des tâches telles que les suivantes :
+ Transmission d'une liste de clés dans une seule requête et renvoi des résultats à partir d'une table
+ Lecture des enregistrements à partir d'une ou plusieurs tables dans une seule requête
+ Écriture des enregistrements en bloc dans une ou plusieurs tables
+ Écriture ou suppression des enregistrements sous condition dans plusieurs tables pouvant avoir une relation

L'utilisation d'opérations par lots avec AWS AppSync DynamoDB est une technique avancée qui nécessite un peu plus de réflexion et de connaissance des opérations de votre backend et des structures de tables. En outre, les opérations par lots AWS AppSync présentent deux différences majeures par rapport aux opérations non groupées :
+ Le rôle de la source de données doit disposer d'autorisations sur toutes les tables auxquelles le résolveur doit accéder.
+ La spécification de table d'un résolveur fait partie du modèle de mappage.

## Permissions
<a name="permissions"></a>

Comme les autres résolveurs, vous devez créer une source de données dans AWS AppSync et soit créer un rôle, soit utiliser un rôle existant. Les opérations par lots nécessitant des autorisations différentes sur les tables DynamoDB, vous devez accorder au rôle configuré des autorisations pour les actions de lecture ou d'écriture :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME",
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME/*"
            ]
        }
    ]
}
```

------

 **Remarque** : Les rôles sont liés aux sources de données dans une source de données AWS AppSync, et les résolveurs de champs sont invoqués par rapport à une source de données. Pour simplifier la configuration, une seule table est spécifiée pour les sources de données configurées pour effectuer une extraction par rapport à DynamoDB. Par conséquent, lorsque vous effectuez une opération de traitement par lots sur plusieurs tables dans un seul résolveur (ce qui constitue une tâche plus avancée), vous devez accorder au rôle associé à cette source de données l'accès à toutes les tables avec lesquelles le résolveur devra interagir. Cela doit être effectué dans le champ **Resource (Ressource)** dans la stratégie IAM ci-dessus. La configuration des tables sur lesquelles les appels par lots doivent être effectués doit être définie dans le modèle de résolveur, dont vous trouverez la description ci-dessous.

## Source de données
<a name="data-source"></a>

Dans un souci de simplicité, nous allons utiliser la même source de données pour tous les résolveurs utilisés dans ce didacticiel. Dans l'onglet **Sources de données**, créez une nouvelle source de données DynamoDB et nommez-la. **BatchTutorial** Le nom de la table peut être quelconque, car les noms de table sont spécifiés dans le cadre du modèle de mappage de demande pour les opérations par lots. Nous allons nommer la table `empty`.

Dans le cadre de ce didacticiel, n'importe quel rôle avec la stratégie en ligne suivante fonctionne :

## Traitement par lots sur une table unique
<a name="single-table-batch"></a>

**Avertissement**  
`BatchPutItem`et ne `BatchDeleteItem` sont pas pris en charge lorsqu'ils sont utilisés avec la détection et la résolution de conflits. Ces paramètres doivent être désactivés pour éviter d'éventuelles erreurs.

Pour cet exemple, supposons que vous ayez une seule table nommée **Posts** dans laquelle vous souhaitez ajouter et supprimer des éléments via des opérations par lots. Utilisez le schéma suivant, en notant que pour la requête, nous transmettrons une liste de IDs :

```
type Post {
    id: ID!
    title: String
}

input PostInput {
    id: ID!
    title: String
}

type Query {
    batchGet(ids: [ID]): [Post]
}

type Mutation {
    batchAdd(posts: [PostInput]): [Post]
    batchDelete(ids: [ID]): [Post]
}

schema {
    query: Query
    mutation: Mutation
}
```

Attachez un résolveur au champ `batchAdd()` avec le **modèle de mappage de demande** suivant. Ce processus prend automatiquement chaque élément du type GraphQL `input PostInput` et crée une mappe, qui est nécessaire pour l'opération `BatchPutItem` :

```
#set($postsdata = [])
#foreach($item in ${ctx.args.posts})
    $util.qr($postsdata.add($util.dynamodb.toMapValues($item)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        "Posts": $utils.toJson($postsdata)
    }
}
```

Dans cet exemple, le **modèle de mappage de réponse** est une simple transmission, mais le nom de la table est ajouté sous la forme `..data.Posts` à l'objet de contexte :

```
$util.toJson($ctx.result.data.Posts)
```

À présent, accédez à la page **Requêtes** de la console AWS AppSync et exécutez la mutation **batchAdd** suivante :

```
mutation add {
    batchAdd(posts:[{
            id: 1 title: "Running in the Park"},{
            id: 2 title: "Playing fetch"
        }]){
            id
            title
    }
}
```

**Vous devriez voir les résultats imprimés à l'écran et vous pouvez valider indépendamment via la console DynamoDB que les deux valeurs ont été écrites dans la table Posts.**

Attachez ensuite un résolveur au champ `batchGet()` avec le **modèle de mappage de demande** suivant. Ce processus prend automatiquement chaque élément du type GraphQL `ids:[]` et crée une mappe, qui est nécessaire pour l'opération `BatchGetItem` :

```
#set($ids = [])
#foreach($id in ${ctx.args.ids})
    #set($map = {})
    $util.qr($map.put("id", $util.dynamodb.toString($id)))
    $util.qr($ids.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchGetItem",
    "tables" : {
        "Posts": {
            "keys": $util.toJson($ids),
            "consistentRead": true,
            "projection" : {
                "expression" : "#id, title",
                "expressionNames" : { "#id" : "id"}
                }
        }
    }
}
```

Le **modèle de mappage de réponse** est à nouveau une simple transmission, avec une fois encore le nom de la table ajouté sous la forme `..data.Posts` à l'objet de contexte :

```
$util.toJson($ctx.result.data.Posts)
```

À présent, revenez à la page **Requêtes** de la console AWS AppSync et exécutez la requête **batchGet** suivante :

```
query get {
    batchGet(ids:[1,2,3]){
        id
        title
    }
}
```

Vous devez obtenir les résultats des deux valeurs `id` que vous avez ajoutées précédemment. Notez la valeur `null` renvoyée pour l'élément `id` de valeur `3`. Cela s'explique par le fait qu'il n'y avait encore aucun enregistrement de votre table **Posts** ayant cette valeur. Notez également que cela AWS AppSync renvoie les résultats dans le même ordre que les clés transmises à la requête, ce qui est une fonctionnalité supplémentaire qui le AWS AppSync fait en votre nom. Par conséquent, si vous basculez vers `batchGet(ids:[1,3,2)`, vous verrez l'ordre modifié. Vous saurez également quel `id` a renvoyé une valeur `null`.

Enfin, attachez un résolveur au champ `batchDelete()` avec le **modèle de mappage de demande** suivant. Ce processus prend automatiquement chaque élément du type GraphQL `ids:[]` et crée une mappe, qui est nécessaire pour l'opération `BatchGetItem` :

```
#set($ids = [])
#foreach($id in ${ctx.args.ids})
    #set($map = {})
    $util.qr($map.put("id", $util.dynamodb.toString($id)))
    $util.qr($ids.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchDeleteItem",
    "tables" : {
        "Posts": $util.toJson($ids)
    }
}
```

Le **modèle de mappage de réponse** est à nouveau une simple transmission, avec une fois encore le nom de la table ajouté sous la forme `..data.Posts` à l'objet de contexte :

```
$util.toJson($ctx.result.data.Posts)
```

À présent, revenez à la page **Requêtes** de la console AWS AppSync et exécutez la mutation **batchDelete** suivante :

```
mutation delete {
    batchDelete(ids:[1,2]){ id }
}
```

Les enregistrements contenant les `id` `1` et `2` doivent désormais avoir été supprimés. Si vous exécutez à nouveau la requête `batchGet()` à partir de l'état précédent, le résultat renvoyé devrait être `null`.

## Traitement par lots sur plusieurs tables
<a name="multi-table-batch"></a>

**Avertissement**  
`BatchPutItem`et ne `BatchDeleteItem` sont pas pris en charge lorsqu'ils sont utilisés avec la détection et la résolution de conflits. Ces paramètres doivent être désactivés pour éviter d'éventuelles erreurs.

AWS AppSync vous permet également d'effectuer des opérations par lots sur plusieurs tables. Créons un application plus complexe. Imaginez que nous construisions une application traitant de la santé des animaux domestiques (nommée Pet Health), où des capteurs signalent l'emplacement et la température corporelle des animaux. Les capteurs sont alimentés par piles et tentent de se connecter au réseau toutes les deux ou trois minutes. Lorsqu'un capteur établit une connexion, il envoie ses relevés à notre AWS AppSync API. Des déclencheurs analysent alors les données afin de pouvoir transmettre un tableau de bord au propriétaire de l'animal. Concentrons-nous sur la représentation des interactions entre le capteur et le magasin de données backend.

**Comme condition préalable, créons d'abord deux tables DynamoDB : **LocationReadings stockera les relevés** de position des capteurs et TemperatureReadings stockera les relevés de température des capteurs.** Les deux tables partagent la même structure de clé primaire : `sensorId (String)` étant la clé de partition et `timestamp (String)` étant la clé de tri.

Nous allons utiliser le schéma GraphQL suivant :

```
type Mutation {
    # Register a batch of readings
    recordReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
    # Delete a batch of readings
    deleteReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
}

type Query {
    # Retrieve all possible readings recorded by a sensor at a specific time
    getReadings(sensorId: ID!, timestamp: String!): [SensorReading]
}

type RecordResult {
    temperatureReadings: [TemperatureReading]
    locationReadings: [LocationReading]
}

interface SensorReading {
    sensorId: ID!
    timestamp: String!
}

# Sensor reading representing the sensor temperature (in Fahrenheit)
type TemperatureReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    value: Float
}

# Sensor reading representing the sensor location (lat,long)
type LocationReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    lat: Float
    long: Float
}

input TemperatureReadingInput {
    sensorId: ID!
    timestamp: String
    value: Float
}

input LocationReadingInput {
    sensorId: ID!
    timestamp: String
    lat: Float
    long: Float
}
```

### BatchPutItem - Enregistrement des lectures des capteurs
<a name="batchputitem-recording-sensor-readings"></a>

Nos capteurs doivent être en mesure d'envoyer leurs relevés une fois qu'ils sont connectés à Internet. Le champ GraphQL `Mutation.recordReadings` est l'API qu'ils utilisent à cet effet. Nous allons joindre un résolveur pour donner vie à notre API.

Sélectionnez **Attach (Attacher)** en regard du champ `Mutation.recordReadings`. Sur l'écran suivant, sélectionnez la source de données `BatchTutorial` créée au début du didacticiel.

Nous ajoutons ensuite le modèle de mappage de demande suivant :

 **Modèle de mappage de demande** 

```
## Convert tempReadings arguments to DynamoDB objects
#set($tempReadings = [])
#foreach($reading in ${ctx.args.tempReadings})
    $util.qr($tempReadings.add($util.dynamodb.toMapValues($reading)))
#end

## Convert locReadings arguments to DynamoDB objects
#set($locReadings = [])
#foreach($reading in ${ctx.args.locReadings})
    $util.qr($locReadings.add($util.dynamodb.toMapValues($reading)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        "locationReadings": $utils.toJson($locReadings),
        "temperatureReadings": $utils.toJson($tempReadings)
    }
}
```

Comme vous pouvez le voir, l'opération `BatchPutItem` nous permet de spécifier plusieurs tables.

Nous utilisons ensuite le modèle de mappage de réponse suivant :

 **Modèle de mappage de réponse** 

```
## If there was an error with the invocation
## there might have been partial results
#if($ctx.error)
    ## Append a GraphQL error for that field in the GraphQL response
    $utils.appendError($ctx.error.message, $ctx.error.message)
#end
## Also returns data for the field in the GraphQL response
$utils.toJson($ctx.result.data)
```

Les opérations par lots peuvent renvoyer à la fois des erreurs et des résultats à la suite de l'appel. Dans ce cas, nous pouvons effectuer certaines opérations de traitement des erreurs supplémentaires.

 **Remarque** : L'utilisation de `$utils.appendError()` est similaire à celle de `$util.error()`, la principale différence résidant dans le fait qu'il n'interrompt pas l'évaluation du modèle de mappage. Au lieu de cela, il signale qu'une erreur s'est produite avec le champ, mais autorise l'évaluation du modèle et renvoie les données à l'appelant. Nous vous recommandons d'utiliser `$utils.appendError()` lorsque votre application doit renvoyer des résultats partiels.

Enregistrez le résolveur et accédez à la page **Requêtes** de la AWS AppSync console. Nous allons maintenant envoyer quelques relevés des capteurs.

Exécutez la mutation suivante :

```
mutation sendReadings {
  recordReadings(
    tempReadings: [
      {sensorId: 1, value: 85.5, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, value: 85.7, timestamp: "2018-02-01T17:21:06.000+08:00"},
      {sensorId: 1, value: 85.8, timestamp: "2018-02-01T17:21:07.000+08:00"},
      {sensorId: 1, value: 84.2, timestamp: "2018-02-01T17:21:08.000+08:00"},
      {sensorId: 1, value: 81.5, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]
    locReadings: [
      {sensorId: 1, lat: 47.615063, long: -122.333551, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, lat: 47.615163, long: -122.333552, timestamp: "2018-02-01T17:21:06.000+08:00"}
      {sensorId: 1, lat: 47.615263, long: -122.333553, timestamp: "2018-02-01T17:21:07.000+08:00"}
      {sensorId: 1, lat: 47.615363, long: -122.333554, timestamp: "2018-02-01T17:21:08.000+08:00"}
      {sensorId: 1, lat: 47.615463, long: -122.333555, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

Nous avons envoyé 10 relevés de capteurs dans une mutation, les relevés étant répartis entre deux tables. **Utilisez la console DynamoDB pour vérifier que les données apparaissent à la fois dans les tables **LocationReadings** et TemperatureReadings.**

### BatchDeleteItem - Suppression des relevés du capteur
<a name="batchdeleteitem-deleting-sensor-readings"></a>

Nous pouvons aussi avoir besoin de supprimer des lots de relevés de capteurs. Nous allons utiliser le champ GraphQL `Mutation.deleteReadings` à cet effet. Sélectionnez **Attach (Attacher)** en regard du champ `Mutation.recordReadings`. Sur l'écran suivant, sélectionnez la source de données `BatchTutorial` créée au début du didacticiel.

Utilisons ensuite le modèle de mappage de demande suivant :

 **Modèle de mappage de demande** 

```
## Convert tempReadings arguments to DynamoDB primary keys
#set($tempReadings = [])
#foreach($reading in ${ctx.args.tempReadings})
    #set($pkey = {})
    $util.qr($pkey.put("sensorId", $reading.sensorId))
    $util.qr($pkey.put("timestamp", $reading.timestamp))
    $util.qr($tempReadings.add($util.dynamodb.toMapValues($pkey)))
#end

## Convert locReadings arguments to DynamoDB primary keys
#set($locReadings = [])
#foreach($reading in ${ctx.args.locReadings})
    #set($pkey = {})
    $util.qr($pkey.put("sensorId", $reading.sensorId))
    $util.qr($pkey.put("timestamp", $reading.timestamp))
    $util.qr($locReadings.add($util.dynamodb.toMapValues($pkey)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchDeleteItem",
    "tables" : {
        "locationReadings": $utils.toJson($locReadings),
        "temperatureReadings": $utils.toJson($tempReadings)
    }
}
```

Le modèle de mappage de réponse est le même que celui utilisé pour `Mutation.recordReadings`.

 **Modèle de mappage de réponse** 

```
## If there was an error with the invocation
## there might have been partial results
#if($ctx.error)
    ## Append a GraphQL error for that field in the GraphQL response
    $utils.appendError($ctx.error.message, $ctx.error.message)
#end
## Also return data for the field in the GraphQL response
$utils.toJson($ctx.result.data)
```

Enregistrez le résolveur et accédez à la page **Requêtes** de la AWS AppSync console. Maintenant, nous allons supprimer un ou deux relevés de capteurs.

Exécutez la mutation suivante :

```
mutation deleteReadings {
  # Let's delete the first two readings we recorded
  deleteReadings(
    tempReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]
    locReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

**Vérifiez via la console DynamoDB que ces deux relevés ont été supprimés des tables **LocationReadings et TemperatureReadings**.**

### BatchGetItem - Récupérez les lectures
<a name="batchgetitem-retrieve-readings"></a>

Une autre opération courante pour l'application Pet Health consiste à récupérer les relevés d'un capteur à un instant précis. Nous allons joindre un résolveur au champ GraphQL `Query.getReadings` dans notre schéma. Sélectionnez **Attach (Attacher)** et, sur l'écran suivant, sélectionnez la source de données `BatchTutorial` créée au début du didacticiel.

Nous ajoutons ensuite le modèle de mappage de demande suivant :

 **Modèle de mappage de demande** 

```
## Build a single DynamoDB primary key,
## as both locationReadings and tempReadings tables
## share the same primary key structure
#set($pkey = {})
$util.qr($pkey.put("sensorId", $ctx.args.sensorId))
$util.qr($pkey.put("timestamp", $ctx.args.timestamp))

{
    "version" : "2018-05-29",
    "operation" : "BatchGetItem",
    "tables" : {
        "locationReadings": {
            "keys": [$util.dynamodb.toMapValuesJson($pkey)],
            "consistentRead": true
        },
        "temperatureReadings": {
            "keys": [$util.dynamodb.toMapValuesJson($pkey)],
            "consistentRead": true
        }
    }
}
```

Notez que nous utilisons maintenant l'**BatchGetItem**opération.

Notre modèle de mappage de réponse va être légèrement différent, car nous avons choisi de renvoyer une liste `SensorReading`. Nous allons mapper le résultat de l'appel au format souhaité.

 **Modèle de mappage de réponse** 

```
## Merge locationReadings and temperatureReadings
## into a single list
## __typename needed as schema uses an interface
#set($sensorReadings = [])

#foreach($locReading in $ctx.result.data.locationReadings)
    $util.qr($locReading.put("__typename", "LocationReading"))
    $util.qr($sensorReadings.add($locReading))
#end

#foreach($tempReading in $ctx.result.data.temperatureReadings)
    $util.qr($tempReading.put("__typename", "TemperatureReading"))
    $util.qr($sensorReadings.add($tempReading))
#end

$util.toJson($sensorReadings)
```

Enregistrez le résolveur et accédez à la page **Requêtes** de la AWS AppSync console. Maintenant, nous allons récupérer les relevés des capteurs.

Exécutez la requête suivante :

```
query getReadingsForSensorAndTime {
  # Let's retrieve the very first two readings
  getReadings(sensorId: 1, timestamp: "2018-02-01T17:21:06.000+08:00") {
    sensorId
    timestamp
    ...on TemperatureReading {
      value
    }
    ...on LocationReading {
      lat
      long
    }
  }
}
```

Nous avons démontré avec succès l'utilisation des opérations par lots DynamoDB en utilisant. AWS AppSync

## Gestion des erreurs
<a name="error-handling"></a>

Dans AWS AppSync, les opérations de source de données peuvent parfois renvoyer des résultats partiels. Le terme résultats partiels est le terme que nous allons utiliser pour désigner une sortie d'opération composée de données et d'une erreur. Étant donné que la gestion des erreurs est par nature spécifique à l'application, AWS AppSync vous donne la possibilité de gérer les erreurs dans le modèle de mappage de réponse. L'erreur d'appel du résolveur, le cas échéant, est disponible depuis le contexte sous la forme `$ctx.error`. Les erreurs d'appel incluent toujours un message et un type, accessibles sous la forme des propriétés `$ctx.error.message` et `$ctx.error.type`. Pendant l'appel du modèle de mappage de réponse, vous pouvez gérer les résultats partiels de trois manières :

1. Par la digestion de l'erreur d'appel en renvoyant simplement les données.

1. Par le déclenchement d'une erreur (en utilisant `$util.error(...)`) en arrêtant l'évaluation du modèle de mappage de réponse, qui ne renvoie alors aucune donnée.

1. Par l'ajout d'une erreur (en utilisant `$util.appendError(...)`) tout en renvoyant les données.

Nous allons présenter chacun des trois points ci-dessus avec les opérations par lots DynamoDB.

### Opérations par lots DynamoDB
<a name="dynamodb-batch-operations"></a>

Dans le cas des opérations par lots DynamoDB, il est possible qu'un lot ne soit exécuté que partiellement. En d'autres termes, il est possible que certains des éléments ou des clés demandés ne soient pas traités. S'il n' AWS AppSync est pas possible de terminer un lot, les éléments non traités et une erreur d'invocation seront définis dans le contexte.

Nous allons mettre en œuvre la gestion des erreurs à l'aide de la configuration de champ `Query.getReadings` de l'opération `BatchGetItem` provenant de la section précédente de ce didacticiel. Cette fois, nous allons supposer que, lors de l'exécution du champ `Query.getReadings`, la table DynamoDB `temperatureReadings` a dépassé le débit alloué. DynamoDB a généré **ProvisionedThroughputExceededException**un à la deuxième tentative pour traiter AWS AppSync les éléments restants du lot.

Le code JSON suivant représente le contexte sérialisé après l'appel de lot DynamoDB, mais avant l'évaluation du modèle de mappage de réponse.

```
{
  "arguments": {
    "sensorId": "1",
    "timestamp": "2018-02-01T17:21:05.000+08:00"
  },
  "source": null,
  "result": {
    "data": {
      "temperatureReadings": [
        null
      ],
      "locationReadings": [
        {
          "lat": 47.615063,
          "long": -122.333551,
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ]
    },
    "unprocessedKeys": {
      "temperatureReadings": [
        {
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ],
      "locationReadings": []
    }
  },
  "error": {
    "type": "DynamoDB:ProvisionedThroughputExceededException",
    "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
  },
  "outErrors": []
}
```

Quelques points à noter concernant le contexte :
+ l'erreur d'appel a été définie sur le contexte à `$ctx.error` by AWS AppSync, et le type d'erreur a été défini sur **DynamoDB** :. ProvisionedThroughputExceededException
+ Les résultats sont mappés par table sous `$ctx.result.data`, même si une erreur est présente.
+ Les clés qui n'ont pas été traitées sont disponibles à l'adresse `$ctx.result.data.unprocessedKeys`. Ici, AWS AppSync impossible de récupérer l'élément avec la clé (SensorID:1, Timestamp:2018-02-01T 17:21:05.000 \$1 08:00) en raison d'un débit de table insuffisant.

 **Remarque** : Pour `BatchPutItem`, la valeur est `$ctx.result.data.unprocessedItems`. Pour `BatchDeleteItem`, la valeur est `$ctx.result.data.unprocessedKeys`.

Nous allons traiter cette erreur de trois façons différentes.

#### 1. Digestion de l'erreur d'appel
<a name="swallowing-the-invocation-error"></a>

Le renvoi des données sans gestion de l'erreur d'appel se traduit par une digestion de l'erreur, ce qui permet au résultat du champ GraphQL donné d'être toujours réussi.

Le modèle de mappage de réponse que nous écrivons est courant et ne se concentre que sur les données de résultat.

Modèle de mappage de réponse :

```
$util.toJson($ctx.result.data)
```

Réponse GraphQL :

```
{
  "data": {
    "getReadings": [
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "lat": 47.615063,
        "long": -122.333551
      },
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  }
}
```

Aucune erreur n'est ajoutée à la réponse d'erreur car l'action n'a porté que sur les données.

#### 2. Déclenchement d'une erreur pour interrompre l'exécution du modèle
<a name="raising-an-error-to-abort-the-template-execution"></a>

Lorsque les défaillances partielles doivent être traitées comme des défaillances complètes du point de vue du client, vous pouvez abandonner l'exécution du modèle pour empêcher le renvoi des données. La méthode d'utilitaire `$util.error(...)` permet d'obtenir exactement ce comportement.

Modèle de mappage de réponse :

```
## there was an error let's mark the entire field
## as failed and do not return any data back in the response
#if ($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type, null, $ctx.result.data.unprocessedKeys)
#end

$util.toJson($ctx.result.data)
```

Réponse GraphQL :

```
{
  "data": {
    "getReadings": null
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```

Même si certains résultats peuvent avoir été renvoyés par l'opération de traitement par lots DynamoDB, nous avons choisi de déclencher une erreur se traduisant par une valeur null pour le champ GraphQL `getReadings` et l'erreur a été ajoutée au bloc d'*erreurs* de la réponse GraphQL.

#### 3. Ajout d'une erreur pour renvoyer à la fois les données et les erreurs
<a name="appending-an-error-to-return-both-data-and-errors"></a>

Dans certains cas, afin d'offrir une meilleure expérience utilisateur, les applications peuvent renvoyer des résultats partiels et informer leurs clients des éléments non traités. Les clients peuvent choisir d'implémenter une nouvelle tentative ou de renvoyer l'erreur à l'utilisateur final. C'est la méthode d'utilitaire `$util.appendError(...)` qui permet d'obtenir ce comportement en laissant le concepteur de l'application ajouter les erreurs au contexte sans interférer avec l'évaluation du modèle. Après avoir évalué le modèle, AWS AppSync traitera toutes les erreurs de contexte en les ajoutant au bloc d'erreurs de la réponse GraphQL.

Modèle de mappage de réponse :

```
#if ($ctx.error)
    ## pass the unprocessed keys back to the caller via the `errorInfo` field
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.data.unprocessedKeys)
#end

$util.toJson($ctx.result.data)
```

Nous avons transmis à la fois l'erreur d'appel et l'élément unprocessedKeys dans le bloc d'erreurs de la réponse GraphQL. Le champ `getReadings` renvoie également les données partielles à partir de la table **locationReadings** comme vous pouvez le voir dans la réponse ci-dessous.

Réponse GraphQL :

```
{
  "data": {
    "getReadings": [
      null,
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```

# Exécution de transactions DynamoDB dans AWS AppSync
<a name="tutorial-dynamodb-transact"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync prend en charge l'utilisation des opérations de transaction Amazon DynamoDB sur une ou plusieurs tables d'une même région. Les opérations prises en charge sont `TransactGetItems` et `TransactWriteItems`. En utilisant ces fonctionnalités dans AWS AppSync, vous pouvez effectuer des tâches telles que :
+ Transmission d'une liste de clés dans une seule requête et renvoi des résultats à partir d'une table
+ Lecture des enregistrements à partir d'une ou plusieurs tables dans une seule requête
+ Écrire les enregistrements d'une transaction sur une ou plusieurs tables d'une all-or-nothing manière ou d'une autre
+ Exécuter des transactions lorsque certaines conditions sont remplies

## Permissions
<a name="permissions"></a>

Comme les autres résolveurs, vous devez créer une source de données dans AWS AppSync et soit créer un rôle, soit utiliser un rôle existant. Les opérations de transaction nécessitant des autorisations différentes sur les tables DynamoDB, vous devez accorder au rôle configuré des autorisations pour les actions de lecture ou d'écriture :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME",
                "arn:aws:dynamodb:us-east-1:111122223333:table/TABLENAME/*"
            ]
        }
    ]
}
```

------

 **Remarque** : Les rôles sont liés aux sources de données dans une source de données AWS AppSync, et les résolveurs de champs sont invoqués par rapport à une source de données. Pour simplifier la configuration, une seule table est spécifiée pour les sources de données configurées pour effectuer une extraction par rapport à DynamoDB. Par conséquent, lorsque vous effectuez une opération de transaction sur plusieurs tables dans un seul résolveur (ce qui constitue une tâche plus avancée), vous devez accorder au rôle associé à cette source de données l'accès à toutes les tables avec lesquelles le résolveur devra interagir. Cela doit être effectué dans le champ **Resource (Ressource)** dans la stratégie IAM ci-dessus. La configuration des appels de transactions sur les tables doit être effectuée dans le modèle de résolveur, dont vous trouverez la description ci-dessous.

## Source de données
<a name="data-source"></a>

Dans un souci de simplicité, nous allons utiliser la même source de données pour tous les résolveurs utilisés dans ce didacticiel. Dans l'onglet **Sources de données**, créez une nouvelle source de données DynamoDB et nommez-la. **TransactTutorial** Le nom de la table peut être quelconque, car les noms de table sont spécifiés dans le cadre du modèle de mappage de demande pour les opérations de transaction. Nous allons nommer la table `empty`.

Nous aurons deux tables appelées **SavingAccounts** et **CheckingAccounts**, les deux avec `accountNumber` comme clé de partition, et une table **TransactionHistory** avec `transactionId` comme clé de partition.

Dans le cadre de ce didacticiel, n'importe quel rôle avec la stratégie en ligne suivante fonctionne. Remplacez `region` et `accountId` par votre région et votre numéro de compte :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/savingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts",
                "arn:aws:dynamodb:us-east-1:111122223333:table/checkingAccounts/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory",
                "arn:aws:dynamodb:us-east-1:111122223333:table/transactionHistory/*"
            ]
        }
    ]
}
```

------

## Transactions
<a name="transactions"></a>

Pour cet exemple, le contexte est une transaction bancaire classique, où nous allons utiliser `TransactWriteItems` pour :
+ Transférer de l'argent des comptes d'épargne vers des comptes de contrôle
+ Générer de nouveaux enregistrements de transaction pour chaque transaction

Ensuite, nous allons utiliser `TransactGetItems` pour récupérer les détails des comptes d'enregistrement et des comptes de vérification.

**Avertissement**  
`TransactWriteItems`n'est pas pris en charge lorsqu'il est utilisé avec la détection et la résolution de conflits. Ces paramètres doivent être désactivés pour éviter d'éventuelles erreurs.

Nous définissons notre schéma GraphQL comme suit :

```
type SavingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type CheckingAccount {
    accountNumber: String!
    username: String
    balance: Float
}

type TransactionHistory {
    transactionId: ID!
    from: String
    to: String
    amount: Float
}

type TransactionResult {
    savingAccounts: [SavingAccount]
    checkingAccounts: [CheckingAccount]
    transactionHistory: [TransactionHistory]
}

input SavingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input CheckingAccountInput {
    accountNumber: String!
    username: String
    balance: Float
}

input TransactionInput {
    savingAccountNumber: String!
    checkingAccountNumber: String!
    amount: Float!
}

type Query {
    getAccounts(savingAccountNumbers: [String], checkingAccountNumbers: [String]): TransactionResult
}

type Mutation {
    populateAccounts(savingAccounts: [SavingAccountInput], checkingAccounts: [CheckingAccountInput]): TransactionResult
    transferMoney(transactions: [TransactionInput]): TransactionResult
}

schema {
    query: Query
    mutation: Mutation
}
```

### TransactWriteItems - Remplir les comptes
<a name="transactwriteitems-populate-accounts"></a>

Afin de transférer de l'argent entre les comptes, nous devons remplir la table avec les détails. Nous allons utiliser l'opération GraphQL `Mutation.populateAccounts` pour le faire.

Dans la section Schéma, cliquez sur **Joindre** à côté de l'`Mutation.populateAccounts`opération. Accédez à VTL Unit Resolvers, puis choisissez la même source de `TransactTutorial` données.

Maintenant, utilisez le modèle de mappage de demande suivant :

 **Modèle de mappage de demande** 

```
#set($savingAccountTransactPutItems = [])
#set($index = 0)
#foreach($savingAccount in ${ctx.args.savingAccounts})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($savingAccount.accountNumber)))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("username", $util.dynamodb.toString($savingAccount.username)))
    $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($savingAccount.balance)))
    #set($index = $index + 1)
    #set($savingAccountTransactPutItem = {"table": "savingAccounts",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($savingAccountTransactPutItems.add($savingAccountTransactPutItem))
#end

#set($checkingAccountTransactPutItems = [])
#set($index = 0)
#foreach($checkingAccount in ${ctx.args.checkingAccounts})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($checkingAccount.accountNumber)))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("username", $util.dynamodb.toString($checkingAccount.username)))
    $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($checkingAccount.balance)))
    #set($index = $index + 1)
    #set($checkingAccountTransactPutItem = {"table": "checkingAccounts",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($checkingAccountTransactPutItems.add($checkingAccountTransactPutItem))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountTransactPutItems))
$util.qr($transactItems.addAll($checkingAccountTransactPutItems))

{
    "version" : "2018-05-29",
    "operation" : "TransactWriteItems",
    "transactItems" : $util.toJson($transactItems)
}
```

Selon le modèle de mappage de réponse suivant :

 **Modèle de mappage de réponse** 

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..5])
    $util.qr($checkingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))

$util.toJson($transactionResult)
```

Enregistrez le résolveur et accédez à la section **Requêtes** de la AWS AppSync console pour renseigner les comptes.

Exécutez la mutation suivante :

```
mutation populateAccounts {
  populateAccounts (
    savingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 100},
      {accountNumber: "2", username: "Amy", balance: 90},
      {accountNumber: "3", username: "Lily", balance: 80},
    ]
    checkingAccounts: [
      {accountNumber: "1", username: "Tom", balance: 70},
      {accountNumber: "2", username: "Amy", balance: 60},
      {accountNumber: "3", username: "Lily", balance: 50},
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
  }
}
```

Nous avons rempli 3 comptes d'épargne et 3 comptes de vérification en une seule mutation.

**Utilisez la console DynamoDB pour vérifier que les données apparaissent à la fois dans les tables SavingAccounts et **CheckingAccounts**.**

### TransactWriteItems - Transférer de l'argent
<a name="transactwriteitems-transfer-money"></a>

Attachez un résolveur au champ `transferMoney` avec le **modèle de mappage de demande** suivant. Notez que les valeurs de `amounts`, `savingAccountNumbers` et `checkingAccountNumbers` sont les mêmes.

```
#set($amounts = [])
#foreach($transaction in ${ctx.args.transactions})
    #set($attributeValueMap = {})
    $util.qr($attributeValueMap.put(":amount", $util.dynamodb.toNumber($transaction.amount)))
    $util.qr($amounts.add($attributeValueMap))
#end

#set($savingAccountTransactUpdateItems = [])
#set($index = 0)
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.savingAccountNumber)))
    #set($update = {})
    $util.qr($update.put("expression", "SET balance = balance - :amount"))
    $util.qr($update.put("expressionValues", $amounts[$index]))
    #set($index = $index + 1)
    #set($savingAccountTransactUpdateItem = {"table": "savingAccounts",
        "operation": "UpdateItem",
        "key": $keyMap,
        "update": $update})
    $util.qr($savingAccountTransactUpdateItems.add($savingAccountTransactUpdateItem))
#end

#set($checkingAccountTransactUpdateItems = [])
#set($index = 0)
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.checkingAccountNumber)))
    #set($update = {})
    $util.qr($update.put("expression", "SET balance = balance + :amount"))
    $util.qr($update.put("expressionValues", $amounts[$index]))
    #set($index = $index + 1)
    #set($checkingAccountTransactUpdateItem = {"table": "checkingAccounts",
        "operation": "UpdateItem",
        "key": $keyMap,
        "update": $update})
    $util.qr($checkingAccountTransactUpdateItems.add($checkingAccountTransactUpdateItem))
#end

#set($transactionHistoryTransactPutItems = [])
#foreach($transaction in ${ctx.args.transactions})
    #set($keyMap = {})
    $util.qr($keyMap.put("transactionId", $util.dynamodb.toString(${utils.autoId()})))
    #set($attributeValues = {})
    $util.qr($attributeValues.put("from", $util.dynamodb.toString($transaction.savingAccountNumber)))
    $util.qr($attributeValues.put("to", $util.dynamodb.toString($transaction.checkingAccountNumber)))
    $util.qr($attributeValues.put("amount", $util.dynamodb.toNumber($transaction.amount)))
    #set($transactionHistoryTransactPutItem = {"table": "transactionHistory",
        "operation": "PutItem",
        "key": $keyMap,
        "attributeValues": $attributeValues})
    $util.qr($transactionHistoryTransactPutItems.add($transactionHistoryTransactPutItem))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountTransactUpdateItems))
$util.qr($transactItems.addAll($checkingAccountTransactUpdateItems))
$util.qr($transactItems.addAll($transactionHistoryTransactPutItems))

{
    "version" : "2018-05-29",
    "operation" : "TransactWriteItems",
    "transactItems" : $util.toJson($transactItems)
}
```

Nous aurons 3 transactions bancaires en une seule opération `TransactWriteItems`. Utilisez le **modèle de mappage de réponse suivant** :

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..5])
    $util.qr($checkingAccounts.add(${ctx.result.keys[$index]}))
#end

#set($transactionHistory = [])
#foreach($index in [6..8])
    $util.qr($transactionHistory.add(${ctx.result.keys[$index]}))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))
$util.qr($transactionResult.put('transactionHistory', $transactionHistory))

$util.toJson($transactionResult)
```

Accédez maintenant à la section **Requêtes** de la console AWS AppSync et exécutez la mutation **TransferMoney** comme suit :

```
mutation write {
  transferMoney(
    transactions: [
      {savingAccountNumber: "1", checkingAccountNumber: "1", amount: 7.5},
      {savingAccountNumber: "2", checkingAccountNumber: "2", amount: 6.0},
      {savingAccountNumber: "3", checkingAccountNumber: "3", amount: 3.3}
    ]) {
    savingAccounts {
      accountNumber
    }
    checkingAccounts {
      accountNumber
    }
    transactionHistory {
      transactionId
    }
  }
}
```

Nous avons envoyé 2 transactions bancaires en une seule mutation. ****Utilisez la console DynamoDB pour vérifier que les données apparaissent dans les tables SavingAccounts, **CheckingAccounts** et TransactionHistory.****

### TransactGetItems - Récupérer des comptes
<a name="transactgetitems-retrieve-accounts"></a>

Afin de récupérer les détails des comptes d'enregistrement et de vérification dans une seule requête transactionnelle, nous attacherons un résolveur à l'opération `Query.getAccounts` GraphQL sur notre schéma. Sélectionnez **Joindre**, accédez à VTL Unit Resolvers, puis sur l'écran suivant, sélectionnez la même source de `TransactTutorial` données créée au début du didacticiel. Configurez les modèles comme suit :

 **Modèle de mappage de demande** 

```
#set($savingAccountsTransactGets = [])
#foreach($savingAccountNumber in ${ctx.args.savingAccountNumbers})
    #set($savingAccountKey = {})
    $util.qr($savingAccountKey.put("accountNumber", $util.dynamodb.toString($savingAccountNumber)))
    #set($savingAccountTransactGet = {"table": "savingAccounts", "key": $savingAccountKey})
    $util.qr($savingAccountsTransactGets.add($savingAccountTransactGet))
#end

#set($checkingAccountsTransactGets = [])
#foreach($checkingAccountNumber in ${ctx.args.checkingAccountNumbers})
    #set($checkingAccountKey = {})
    $util.qr($checkingAccountKey.put("accountNumber", $util.dynamodb.toString($checkingAccountNumber)))
    #set($checkingAccountTransactGet = {"table": "checkingAccounts", "key": $checkingAccountKey})
    $util.qr($checkingAccountsTransactGets.add($checkingAccountTransactGet))
#end

#set($transactItems = [])
$util.qr($transactItems.addAll($savingAccountsTransactGets))
$util.qr($transactItems.addAll($checkingAccountsTransactGets))

{
    "version" : "2018-05-29",
    "operation" : "TransactGetItems",
    "transactItems" : $util.toJson($transactItems)
}
```

 **Modèle de mappage de réponse** 

```
#if ($ctx.error)
    $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons)
#end

#set($savingAccounts = [])
#foreach($index in [0..2])
    $util.qr($savingAccounts.add(${ctx.result.items[$index]}))
#end

#set($checkingAccounts = [])
#foreach($index in [3..4])
    $util.qr($checkingAccounts.add($ctx.result.items[$index]))
#end

#set($transactionResult = {})
$util.qr($transactionResult.put('savingAccounts', $savingAccounts))
$util.qr($transactionResult.put('checkingAccounts', $checkingAccounts))

$util.toJson($transactionResult)
```

Enregistrez le résolveur et accédez aux sections **Requêtes** de la AWS AppSync console. Pour récupérer les comptes d'enregistrement et de vérification, exécutez la requête suivante :

```
query getAccounts {
  getAccounts(
    savingAccountNumbers: ["1", "2", "3"],
    checkingAccountNumbers: ["1", "2"]
  ) {
    savingAccounts {
      accountNumber
      username
      balance
    }
    checkingAccounts {
      accountNumber
      username
      balance
    }
  }
}
```

Nous avons démontré avec succès l'utilisation des transactions DynamoDB en utilisant. AWS AppSync

# Utilisation de résolveurs HTTP dans AWS AppSync
<a name="tutorial-http-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync vous permet d'utiliser des sources de données prises en charge (Amazon DynamoDB AWS Lambda, Amazon Service ou OpenSearch Amazon Aurora) pour effectuer diverses opérations, en plus des points de terminaison HTTP arbitraires pour résoudre les champs GraphQL. Dès que vos points de terminaison HTTP sont disponibles, vous pouvez vous y connecter à l'aide d'une source de données. Ensuite, vous pouvez configurer un résolveur dans le schéma GraphQL pour effectuer des opérations telles que des requêtes, des mutations et des abonnements. Ce didacticiel vous présente certains exemples courants.

Dans ce didacticiel, vous utiliserez une API REST (créée à l'aide d'Amazon API Gateway et Lambda) avec un point de terminaison GraphQL AWS AppSync .

## Configuration en un clic
<a name="one-click-setup"></a>

Si vous souhaitez configurer automatiquement un point de terminaison GraphQL AWS AppSync avec un point de terminaison HTTP configuré (à l'aide d'Amazon API Gateway et de Lambda), vous pouvez utiliser le modèle suivant : AWS CloudFormation 

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-full.yaml)

## Création d'une API REST
<a name="creating-a-rest-api"></a>

Vous pouvez utiliser le AWS CloudFormation modèle suivant pour configurer un point de terminaison REST adapté à ce didacticiel :

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-api-gw.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/http/http-api-gw.yaml)

La AWS CloudFormation pile exécute les étapes suivantes :

1. Elle configure une fonction Lambda qui contient la logique métier de votre microservice.

1. Configure une API REST API Gateway avec la combinaison endpoint/method/content de types suivante :


****  

| Chemin de ressource API | Méthode HTTP | Type de contenu pris en charge | 
| --- | --- | --- | 
|  /v1/users  |  POST  |  application/json  | 
|  /v1/users  |  GET  |  application/json  | 
|  /v1/users/1  |  GET  |  application/json  | 
|  /v1/users/1  |  PUT  |  application/json  | 
|  /v1/users/1  |  DELETE  |  application/json  | 

## Création de votre API GraphQL
<a name="creating-your-graphql-api"></a>

Pour créer l'API GraphQL dans : AWS AppSync
+ Ouvrez la AWS AppSync console et choisissez **Create API**.
+ Pour le nom de l'API, saisissez `UserData`.
+ Choisissez **Schéma personnalisé**.
+ Choisissez **Créer**.

La AWS AppSync console crée une nouvelle API GraphQL pour vous en utilisant le mode d'authentification par clé d'API. Vous pouvez utiliser la console pour configurer le reste de l'API GraphQL et exécuter des requêtes sur celle-ci jusqu'à la fin de ce didacticiel.

## Création d'un schéma GraphQL
<a name="creating-a-graphql-schema"></a>

Maintenant que vous avez une API GraphQL, nous allons créer un schéma GraphQL. Dans l'éditeur de schéma de la AWS AppSync console, assurez-vous que votre schéma correspond au schéma suivant :

```
schema {
    query: Query
    mutation: Mutation
}

type Mutation {
    addUser(userInput: UserInput!): User
    deleteUser(id: ID!): User
}

type Query {
    getUser(id: ID): User
    listUser: [User!]!
}

type User {
    id: ID!
    username: String!
    firstname: String
    lastname: String
    phone: String
    email: String
}

input UserInput {
    id: ID!
    username: String!
    firstname: String
    lastname: String
    phone: String
    email: String
}
```

## Configurez votre source de données HTTP
<a name="configure-your-http-data-source"></a>

Pour configurer votre source de données HTTP, procédez comme suit :
+ **DataSources**Dans l'onglet, choisissez **Nouveau**, puis tapez un nom convivial pour la source de données (par exemple,`HTTP`).
+ Dans **Type de source de données**, choisissez **HTTP**.
+ Définissez le point de terminaison sur le point de terminaison API Gateway créé. Assurez-vous de ne pas inclure le nom de l'étape dans le point de terminaison.

 **Remarque :** à l'heure actuelle, seuls les points de terminaison publics sont pris en charge par AWS AppSync.

 **Remarque :** Pour plus d'informations sur les autorités de certification reconnues par le AWS AppSync service, voir [Autorités de certification (CA) reconnues par AWS AppSync pour les points de terminaison HTTPS](http-cert-authorities.md#aws-appsync-http-certificate-authorities).

## Configuration des résolveurs
<a name="configuring-resolvers"></a>

Au cours de cette étape, vous allez connecter la source de données http à la requête **getUser**.

Pour configurer le résolveur :
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Types de données** à droite sous le champ **Requête**, recherchez le champ **getUser** et choisissez **Joindre**.
+ Dans **Nom de la source de données**, choisissez **HTTP**.
+ Collez le code suivant dans la section **Configurer le modèle de mappage de demande** :

```
{
    "version": "2018-05-29",
    "method": "GET",
    "params": {
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "resourcePath": $util.toJson("/v1/users/${ctx.args.id}")
}
```
+ Collez le code suivant dans la section **Configurer le modèle de mappage de réponse** :

```
## return the body
#if($ctx.result.statusCode == 200)
    ##if response is 200
    $ctx.result.body
#else
    ##if response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Choisissez l'onglet **Requête** et exécutez la requête suivante :

```
query GetUser{
    getUser(id:1){
        id
        username
    }
}
```

Cela doit renvoyer la réponse suivante :

```
{
    "data": {
        "getUser": {
            "id": "1",
            "username": "nadia"
        }
    }
}
```
+ Choisissez l'onglet **Schéma**.
+ Dans le volet **Types de données** à droite sous le champ **Mutation**, recherchez le champ **addUser** et choisissez **Joindre**.
+ Dans **Nom de la source de données**, choisissez **HTTP**.
+ Collez le code suivant dans la section **Configurer le modèle de mappage de demande** :

```
{
    "version": "2018-05-29",
    "method": "POST",
    "resourcePath": "/v1/users",
    "params":{
      "headers":{
        "Content-Type": "application/json",
      },
      "body": $util.toJson($ctx.args.userInput)
    }
}
```
+ Collez le code suivant dans la section **Configurer le modèle de mappage de réponse** :

```
## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
## if the response status code is not 200, then return an error. Else return the body **
#if($ctx.result.statusCode == 200)
    ## If response is 200, return the body.
    $ctx.result.body
#else
    ## If response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Choisissez l'onglet **Requête** et exécutez la requête suivante :

```
mutation addUser{
    addUser(userInput:{
        id:"2",
        username:"shaggy"
    }){
        id
        username
    }
}
```

Cela doit renvoyer la réponse suivante :

```
{
    "data": {
        "getUser": {
        "id": "2",
        "username": "shaggy"
        }
    }
}
```

## Invoquer AWS des services
<a name="invoking-aws-services"></a>

Vous pouvez utiliser des résolveurs HTTP pour configurer une interface AWS d'API GraphQL pour les services. Les requêtes HTTP AWS doivent être signées à l'aide du [processus Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) afin de AWS pouvoir identifier leur expéditeur. AWS AppSync calcule la signature en votre nom lorsque vous associez un rôle IAM à la source de données HTTP.

Vous fournissez deux composants supplémentaires pour appeler AWS des services avec des résolveurs HTTP :
+ Rôle IAM autorisé à appeler le service AWS APIs
+ La configuration de signature dans la source de données

Par exemple, si vous souhaitez appeler l'[ListGraphqlApis opération](https://docs.aws.amazon.com/appsync/latest/APIReference/API_ListGraphqlApis.html) avec des résolveurs HTTP, vous devez d'abord [créer un rôle IAM](attaching-a-data-source.md#aws-appsync-getting-started-build-a-schema-from-scratch) doté AWS AppSync de la politique suivante :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Action": [
                "appsync:ListGraphqlApis"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
```

------

Créez ensuite la source de données HTTP pour AWS AppSync. Dans cet exemple, vous appelez AWS AppSync dans la région USA Ouest (Oregon). Configurez la configuration HTTP suivante dans un fichier nommé `http.json`, qui inclut la région de signature et le nom du service :

```
{
    "endpoint": "https://appsync.us-west-2.amazonaws.com/",
    "authorizationConfig": {
        "authorizationType": "AWS_IAM",
        "awsIamConfig": {
            "signingRegion": "us-west-2",
            "signingServiceName": "appsync"
        }
    }
}
```

Utilisez ensuite le AWS CLI pour créer la source de données avec un rôle associé, comme suit :

```
aws appsync create-data-source --api-id <API-ID> \
                               --name AWSAppSync \
                               --type HTTP \
                               --http-config file:///http.json \
                               --service-role-arn <ROLE-ARN>
```

Lorsque vous attachez un résolveur au champ du schéma, utilisez le modèle de mappage de demandes suivant pour appeler AWS AppSync :

```
{
    "version": "2018-05-29",
    "method": "GET",
    "resourcePath": "/v1/apis"
}
```

Lorsque vous exécutez une requête GraphQL pour cette source de données, signez la AWS AppSync demande en utilisant le rôle que vous avez indiqué et incluez la signature dans la demande. La requête renvoie une liste de AWS AppSync GraphQL présents APIs dans votre compte dans cette AWS région.

# Utilisation d'Aurora Serverless v2 avec AWS AppSync
<a name="tutorial-rds-resolvers"></a>

Connectez votre API GraphQL aux bases de données Aurora Serverless à l'aide de. AWS AppSync Cette intégration vous permet d'exécuter des instructions SQL via des requêtes GraphQL, des mutations et des abonnements, ce qui vous permet d'interagir de manière flexible avec vos données relationnelles.

**Note**  
Ce didacticiel utilise la région `US-EAST-1`.

**Avantages**
+ Intégration parfaite entre GraphQL et les bases de données relationnelles
+ Possibilité d'effectuer des opérations SQL via des interfaces GraphQL
+ Évolutivité sans serveur avec Aurora Serverless v2
+ Accès sécurisé aux données via AWS Secrets Manager
+ Protection contre l'injection de code SQL grâce au nettoyage des entrées
+ Fonctionnalités de requête flexibles, notamment le filtrage et les opérations de plage

**Cas d'utilisation courants**
+ Création d'applications évolutives répondant à des exigences en matière de données relationnelles
+ Création APIs nécessitant à la fois la flexibilité de GraphQL et les fonctionnalités de base de données SQL
+ Gestion des opérations de données par le biais de mutations et de requêtes GraphQL
+ Implémentation de modèles d'accès sécurisés aux bases

Dans ce didacticiel, vous allez apprendre ce qui suit.
+ Configuration d'un cluster Aurora Serverless v2
+ Activer la fonctionnalité de l'API de données
+ Création et configuration de structures de base de données
+ Définition de schémas GraphQL pour les opérations de base de données
+ Implémenter des résolveurs pour les requêtes et les mutations
+ Sécurisez l'accès à vos données grâce à un nettoyage approprié des entrées
+ Exécuter diverses opérations de base de données via les interfaces GraphQL

**Topics**
+ [Configuration de votre cluster de bases de données](#create-cluster)
+ [Activer l'API de données](#enable-data-api)
+ [Créer une base de données et une table](#create-database-and-table)
+ [Schéma GraphQL](#graphql-schema)
+ [Connectez votre API aux opérations de base de données](#configuring-resolvers)
+ [Modifiez vos données via l'API](#run-mutations)
+ [Récupérez vos données](#run-queries)
+ [Sécurisez votre accès aux données](#input-sanitization)

## Configuration de votre cluster de bases de données
<a name="create-cluster"></a>

Avant d'ajouter une source de données Amazon RDS à AWS AppSync, vous devez d'abord activer une API de données sur un cluster Aurora Serverless v2 et **configurer un secret** à l'aide de. *AWS Secrets Manager* Vous pouvez créer un cluster Aurora Serverless v2 à l'aide de AWS CLI :

```
aws rds create-db-cluster \
    --db-cluster-identifier appsync-tutorial \
    --engine aurora-mysql \
    --engine-version 8.0 \
    --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \
    --master-username USERNAME \
    --master-user-password COMPLEX_PASSWORD \
    --enable-http-endpoint
```

Cela renverra un ARN pour le cluster.

Après avoir créé le cluster, vous devez ajouter une instance Aurora Serverless v2 à l'aide de la commande suivante.

```
aws rds create-db-instance \
    --db-cluster-identifier appsync-tutorial \
    --db-instance-identifier appsync-tutorial-instance-1 \
    --db-instance-class db.serverless \
    --engine aurora-mysql
```

**Note**  
L'activation de ces points de terminaison prend du temps. Vous pouvez vérifier leur statut dans la console Amazon RDS dans l'onglet **Connectivité et sécurité** du cluster. Vous pouvez également vérifier l'état de votre cluster à l'aide de la AWS CLI commande suivante.   

```
aws rds describe-db-clusters \
    --db-cluster-identifier appsync-tutorial \
    --query "DBClusters[0].Status"
```

Vous pouvez créer un *secret* à l'aide de la AWS Secrets Manager console ou AWS CLI avec un fichier d'entrée tel que le suivant en utilisant le `USERNAME` et `COMPLEX_PASSWORD` de l'étape précédente.

```
{
    "username": "USERNAME",
    "password": "COMPLEX_PASSWORD"
}
```

Passez ceci en tant que paramètre à AWS CLI :

```
aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1
```

Cela renverra un ARN pour le secret.

 **Notez l'ARN** de votre cluster Aurora Serverless et le code secret pour une utilisation ultérieure dans la AppSync console lors de la création d'une source de données.

## Activer l'API de données
<a name="enable-data-api"></a>

Vous pouvez activer l'API de données sur votre cluster en [suivant les instructions fournies dans la documentation RDS](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html). L'API de données doit être activée avant d'être ajoutée en tant que source de AppSync données.

## Créer une base de données et une table
<a name="create-database-and-table"></a>

Une fois que vous avez activé votre API de données, vous pouvez vous assurer qu'elle fonctionne à l'aide de la `aws rds-data execute-statement` commande du AWS CLI. Cela garantira que votre cluster Aurora Serverless est correctement configuré avant de l'ajouter à votre AppSync API. Créez d'abord une base de données nommée *TESTDB* avec le paramètre `--sql` comme suit :

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \
--schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1"  \
--region us-east-1 --sql "create DATABASE TESTDB"
```

Si elle s'exécute sans erreur, ajoutez une table avec la commande *create table (créer une table)* :

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \
 --schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \
 --region us-east-1 \
 --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"
```

Si tout s'est déroulé sans problème, vous pouvez passer à l'ajout du cluster en tant que source de données dans votre AppSync API.

## Schéma GraphQL
<a name="graphql-schema"></a>

Maintenant que votre API de données Aurora sans serveur est opérationnel avec une table, nous allons créer un schéma GraphQL et attacher des résolveurs pour réaliser des mutations et des abonnements. Créez une nouvelle API dans la AWS AppSync console, accédez à la page **Schéma**, puis entrez les informations suivantes :

```
type Mutation {
    createPet(input: CreatePetInput!): Pet
    updatePet(input: UpdatePetInput!): Pet
    deletePet(input: DeletePetInput!): Pet
}

input CreatePetInput {
    type: PetType
    price: Float!
}

input UpdatePetInput {
id: ID!
    type: PetType
    price: Float!
}

input DeletePetInput {
    id: ID!
}

type Pet {
    id: ID!
    type: PetType
    price: Float
}

enum PetType {
    dog
    cat
    fish
    bird
    gecko
}

type Query {
    getPet(id: ID!): Pet
    listPets: [Pet]
    listPetsByPriceRange(min: Float, max: Float): [Pet]
}

schema {
    query: Query
    mutation: Mutation
}
```

 **Enregistrez** votre schéma et accédez à la page **Data Sources (Sources de données)**, puis créez une nouvelle source de données. Sélectionnez une **Base de données relationnelle** pour le type de source de données et saisissez un nom convivial. Utilisez le nom de la base de données que vous avez créé au cours de l'étape précédente, ainsi que **l'ARN de cluster** que vous avez créé. Pour le **rôle**, vous pouvez soit AppSync créer un nouveau rôle, soit en créer un avec une politique similaire à celle ci-dessous :

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-data:BatchExecuteStatement",
                "rds-data:BeginTransaction",
                "rds-data:CommitTransaction",
                "rds-data:ExecuteStatement",
                "rds-data:RollbackTransaction"
            ],
            "Resource": [
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": [
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
            ]
        }
    ]
}
```

------

Notez qu'il y a deux **Déclarations** dans cette stratégie auxquelles vous accordez l'accès au rôle. La première **ressource** est votre cluster Aurora Serverless et la seconde est votre AWS Secrets Manager ARN. Vous devrez fournir **les DEUX** ARNs dans la configuration de la source de AppSync données avant de cliquer sur **Créer**.

Transmettez-le en tant que paramètre au AWS CLI.

```
aws secretsmanager create-secret \
  --name HttpRDSSecret \
  --secret-string file://creds.json \
  --region us-east-1
```

Cela renverra un ARN pour le secret. Prenez note de l'ARN de votre cluster Aurora Serverless et de Secret pour plus tard lors de la création d'une source de données dans la AWS AppSync console.

### Construisez la structure de votre base de données
<a name="create-database-and-table"></a>

Une fois que vous avez activé votre API de données, vous pouvez vous assurer qu'elle fonctionne à l'aide de la `aws rds-data execute-statement` commande du AWS CLI. Cela garantira que votre cluster Aurora Serverless v2 est correctement configuré avant de l'ajouter à votre AWS AppSync API. Créez d'abord une base de données appelée *TESTDB* avec le `--sql` paramètre suivant.

```
aws rds-data execute-statement \
                --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial" \
                --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret"  \
                --region us-east-1 \
                --sql "create DATABASE TESTDB"
```

Si cela fonctionne sans erreur, ajoutez une table avec la commande *create table* suivante.

```
aws rds-data execute-statement \
      --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:http-endpoint-test" \
      --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:testHttp2-AmNvc1" \
      --region us-east-1 \
      --sql "create table Pets(id varchar(200), type varchar(200), price float)" \
      --database "TESTDB"
```

### Concevez votre interface API
<a name="graphql-schema"></a>

Une fois que l'API de données Aurora Serverless v2 est opérationnelle avec une table, créez un schéma GraphQL et associez des résolveurs pour effectuer des mutations et des abonnements. Créez une nouvelle API dans la AWS AppSync console, accédez à la page **Schéma** de la console, puis entrez ce qui suit.

```
type Mutation {
        createPet(input: CreatePetInput!): Pet
        updatePet(input: UpdatePetInput!): Pet
        deletePet(input: DeletePetInput!): Pet
    }
    
    input CreatePetInput {
        type: PetType
        price: Float!
    }
    
    input UpdatePetInput {
        id: ID!
        type: PetType
        price: Float!
    }
    
    input DeletePetInput {
        id: ID!
    }
    
    type Pet {
        id: ID!
        type: PetType
        price: Float
    }
    
    enum PetType {
        dog
        cat
        fish
        bird
        gecko
    }
    
    type Query {
        getPet(id: ID!): Pet
        listPets: [Pet]
        listPetsByPriceRange(min: Float, max: Float): [Pet]
    }
    
    schema {
        query: Query
        mutation: Mutation
    }
```

 **Enregistrez** votre schéma et accédez à la page **Data Sources (Sources de données)**, puis créez une nouvelle source de données. Choisissez **Base de données relationnelle** pour le type de **source de données** et fournissez un nom convivial. Utilisez le nom de la base de données que vous avez créé au cours de l'étape précédente, ainsi que **l'ARN de cluster** que vous avez créé. Pour le **rôle**, vous pouvez soit AWS AppSync créer un nouveau rôle, soit en créer un avec une politique similaire à la suivante.

------
#### [ JSON ]

****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "rds-data:BatchExecuteStatement",
                    "rds-data:BeginTransaction",
                    "rds-data:CommitTransaction",
                    "rds-data:ExecuteStatement",
                    "rds-data:RollbackTransaction"
                ],
                "Resource": [
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
                ]
            },
            {
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue"
                ],
                "Resource": [
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
                ]
            }
        ]
    }
```

------

Notez qu'il y a deux **Déclarations** dans cette stratégie auxquelles vous accordez l'accès au rôle. La première **ressource** est votre cluster Aurora Serverless v2 et la seconde est votre AWS Secrets Manager ARN. Vous devrez fournir **les DEUX** ARNs dans la configuration de la source de AWS AppSync données avant de cliquer sur **Créer**.

## Connectez votre API aux opérations de base de données
<a name="configuring-resolvers"></a>

Maintenant que nous disposons d'un schéma GraphQL valide et d'une source de données RDS, vous pouvez associer des résolveurs aux champs GraphQL de votre schéma. Notre API proposera les fonctions suivantes :

1. créer un animal de compagnie à l'aide du champ *Mutation.CreatePet*

1. mettre à jour un animal de compagnie à l'aide du champ *Mutation.updatePet*

1. supprimer un animal de compagnie à l'aide du champ *Mutation.deletePet*

1. obtenir un single en utilisant le champ *Query.getPet*

1. tout répertorier à l'aide du champ *Query.listPets*

1. listez les animaux de compagnie dans une fourchette de prix à l'aide de la *requête. listPetsByPriceRange*champ

### Mutation.createPet
<a name="mutation-createpet"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`createPet(input: CreatePetInput!): Pet`. Choisissez votre source de données RDS. Dans la section **request mapping template (modèle de mappage de requête)**, ajoutez le modèle suivant :

```
#set($id=$utils.autoId())
{
"version": "2018-05-29",
    "statements": [
        "insert into Pets VALUES (:ID, :TYPE, :PRICE)",
        "select * from Pets WHERE id = :ID"
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

Le système exécute les instructions SQL de manière séquentielle, en fonction de l'ordre dans le tableau **des instructions**. Les résultats reviendront dans le même ordre. Comme il s'agit d'une mutation, vous exécuterez une instruction *select* après l'*insertion* pour récupérer les valeurs validées afin de remplir le modèle de mappage des réponses GraphQL.

Dans la section **response mapping template (modèle de mappage de réponse)**, ajoutez le modèle suivant :

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

Comme les *déclarations* possèdent deux requêtes SQL, nous devons spécifier le deuxième résultat dans la matrice qui revient de la base de données avec : `$utils.rds.toJsonString($ctx.result))[1][0])`.

### Mutation.updatePet
<a name="mutation-updatepet"></a>

Dans l'éditeur de schéma de la AWS AppSync console, choisissez **Attach Resolver** for`updatePet(input: UpdatePetInput!): Pet`. Choisissez votre **source de données RDS**. Dans la section **Modèle de mappage des demandes**, ajoutez le modèle suivant.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"),
        $util.toJson("select * from Pets WHERE id = :ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

Dans la section **Modèle de mappage des réponses**, ajoutez le modèle suivant.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

### Mutation.deletePet
<a name="mutation-deletepet"></a>

Dans l'éditeur de schéma de la AWS AppSync console, choisissez **Attach Resolver** for`deletePet(input: DeletePetInput!): Pet`. Choisissez votre **source de données RDS**. Dans la section **Modèle de mappage des demandes**, ajoutez le modèle suivant.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("select * from Pets WHERE id=:ID"),
        $util.toJson("delete from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id"
    }
}
```

Dans la section **Modèle de mappage des réponses**, ajoutez le modèle suivant.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.getPet
<a name="query-getpet"></a>

Maintenant que les mutations sont créées pour votre schéma, connectez les trois requêtes pour montrer comment obtenir des éléments individuels, des listes et appliquer un filtrage SQL. Dans l'**éditeur de schéma** de la AWS AppSync console, choisissez **Attach Resolver** for`getPet(id: ID!): Pet`. Choisissez votre **source de données RDS**. Dans la section **Modèle de mappage des demandes**, ajoutez le modèle suivant.

```
{
"version": "2018-05-29",
        "statements": [
            $util.toJson("select * from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.id"
    }
}
```

Dans la section **response mapping template (modèle de mappage de réponse)**, ajoutez le modèle suivant :

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.listPets
<a name="query-listpets"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`getPet(id: ID!): Pet`. Choisissez votre **source de données RDS**. Dans la section **Modèle de mappage des demandes**, ajoutez le modèle suivant.

```
{
    "version": "2018-05-29",
    "statements": [
        "select * from Pets"
    ]
}
```

Dans la section **Modèle de mappage des réponses**, ajoutez le modèle suivant.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

### Requête. listPetsByPriceRange
<a name="query-listpetsbypricerange"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`getPet(id: ID!): Pet`. Choisissez votre **source de données RDS**. Dans la section **Modèle de mappage des demandes**, ajoutez le modèle suivant.

```
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.max),
        ":MIN": $util.toJson($ctx.args.min)
    }
}
```

Dans la section **response mapping template (modèle de mappage de réponse)**, ajoutez le modèle suivant :

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

## Modifiez vos données via l'API
<a name="run-mutations"></a>

Maintenant que vous avez configuré tous vos résolveurs avec les déclarations SQL et connecté votre API GraphQL à votre API de données Aurora sans serveur, vous pouvez commencer l'exécution de mutations et de requêtes. Dans AWS AppSync la console, choisissez l'onglet **Requêtes** et entrez ce qui suit pour créer un animal de compagnie :

```
mutation add {
    createPet(input : { type:fish, price:10.0 }){
        id
        type
        price
    }
}
```

La réponse doit contenir les *id*, *type*, et *prix* comme suit :

```
{
  "data": {
    "createPet": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "type": "fish",
      "price": "10.0"
    }
  }
}
```

Vous pouvez modifier cet élément en exécutant la mutation *updatePet* :

```
mutation update {
    updatePet(input : {
        id: ID_PLACEHOLDER,
        type:bird,
        price:50.0
    }){
        id
        type
        price
    }
}
```

Notez que nous avons utilisé l'*id* qui a été précédemment renvoyé de l'opération *createPet*. Il s'agira d'une valeur unique pour votre enregistrement car le résolveur a exploité `$util.autoId()`. Vous pouvez supprimer un enregistrement de cette façon :

```
mutation delete {
    deletePet(input : {id:ID_PLACEHOLDER}){
        id
        type
        price
    }
}
```

Créez quelques enregistrements avec la première mutation avec des valeurs différentes pour le *prix,*, puis exécutez quelques requêtes.

## Récupérez vos données
<a name="run-queries"></a>

Toujours dans l'onglet **Requêtes** de la console, utilisez l'instruction suivante pour répertorier tous les enregistrements que vous avez créés.

```
query allpets {
    listPets {
        id
        type
        price
    }
}
```

Tirez parti du prédicat SQL *WHERE* contenu `where price > :MIN and price < :MAX` dans notre modèle de mappage pour *Query. listPetsByPriceRange*avec la requête GraphQL suivante :

```
query petsByPriceRange {
    listPetsByPriceRange(min:1, max:11) {
        id
        type
        price
    }
}
```

Vous devez uniquement voir des enregistrements avec un *prix* supérieur à \$11 ou inférieur à \$110. Enfin, vous pouvez effectuer des requêtes pour récupérer des enregistrements spécifiques, comme suit :

```
query onePet {
    getPet(id:ID_PLACEHOLDER){
        id
        type
        price
    }
}
```

## Sécurisez votre accès aux données
<a name="input-sanitization"></a>

L'injection SQL constitue une faille de sécurité dans les applications de base de données. Cela se produit lorsque des attaquants insèrent du code SQL malveillant dans les champs de saisie de l'utilisateur. Cela peut permettre un accès non autorisé aux données de la base de données. Nous vous recommandons de valider et de désinfecter soigneusement toutes les entrées utilisateur avant de les traiter afin de vous protéger contre `variableMap` les attaques par injection de code SQL. Si les cartes de variables ne sont pas utilisées, il vous incombe de nettoyer les arguments de leurs opérations GraphQL. L'une des façons de s'y prendre est de fournir des étapes de validation spécifique d'entrée dans le modèle de mappage de demande avant l'exécution d'une déclaration SQL sur votre API de données. Voyons comment modifier le modèle de mappage de demande de l'exemple `listPetsByPriceRange`. Au lieu de vous baser uniquement sur l'entrée utilisateur, vous pouvez effectuer les actions suivantes :

```
#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice))

#set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice))


#if (!$validMaxPrice || !$validMinPrice)
    $util.error("Provided price input is not valid.")
#end
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.maxPrice),
        ":MIN": $util.toJson($ctx.args.minPrice)
    }
}
```

Une autre façon de vous protéger contre les entrées intruses lors de l'exécution de résolveurs sur vos API de données consiste à utiliser des instructions préparées avec une procédure stockée et des entrées paramétrées. Par exemple, dans le résolveur pour `listPets` définissez la procédure suivante qui exécute *select* comme une instruction préparée :

```
CREATE PROCEDURE listPets (IN type_param VARCHAR(200))
  BEGIN
     PREPARE stmt FROM 'SELECT * FROM Pets where type=?';
     SET @type = type_param;
     EXECUTE stmt USING @type;
     DEALLOCATE PREPARE stmt;
  END
```

Créez-le dans votre instance Aurora Serverless v2.

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \
--schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx"  \
--region us-east-1  --database "DB_NAME" \
--sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"
```

Le code résolveur obtenu pour listPets est simplifié, car désormais nous appelons simplement la procédure stockée. Au minimum, toute entrée de chaîne doit avoir des guillemets simples [précédés d'un caractère d'échappement](#escaped).

```
#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type))
#if (!$validType)
    $util.error("Input for 'type' is not valid.", "ValidationError")
#end

{
    "version": "2018-05-29",
    "statements": [
        "CALL listPets(:type)"
    ]
    "variableMap": {
        ":type": $util.toJson($ctx.args.type.replace("'", "''"))
    }
}
```

### Utilisation de chaînes d'échappement
<a name="escaped"></a>

Utilisez des guillemets simples pour marquer le début et la fin des littéraux de chaîne dans une instruction SQL, par exemple. `'some string value'`. Pour permettre l'utilisation de valeurs de chaîne avec un ou plusieurs guillemets simples (`'`) dans une chaîne, chaque valeur doit être remplacée par deux guillemets simples (`''`). Par exemple, si la chaîne d'entrée est `Nadia's dog`, vous l'échappez pour l'instruction SQL comme

```
update Pets set type='Nadia''s dog' WHERE id='1'
```

# Utilisation de résolveurs de pipeline dans AWS AppSync
<a name="tutorial-pipeline-resolvers"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync fournit un moyen simple de connecter un champ GraphQL à une source de données unique via des résolveurs unitaires. Toutefois, l'exécution d'une seule opération peut ne pas être suffisante. Les résolveurs de pipeline offrent la possibilité d'exécuter des opérations en série sur des sources de données. Créez des fonctions dans votre API et attachez-les à un résolveur de pipeline. L'exécution de chaque fonction est acheminée jusqu'à l'autre, jusqu'à ce qu'il ne reste plus aucune fonction à exécuter. Avec les résolveurs de pipeline, vous pouvez désormais créer des flux de travail plus complexes directement dans AWS AppSync. Dans ce didacticiel, vous construisez une application simple d'affichage de photos, dans laquelle les utilisateurs peuvent publier et visualiser les images publiées par leurs amis.

## Configuration en un clic
<a name="one-click-setup"></a>

Si vous souhaitez configurer automatiquement le point de terminaison GraphQL AWS AppSync avec tous les résolveurs configurés et les AWS ressources nécessaires, vous pouvez utiliser le modèle suivant : AWS CloudFormation 

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml)

Cette pile crée les ressources suivantes dans votre compte :
+ Rôle IAM pour accéder AWS AppSync aux ressources de votre compte
+ 2 tables DynamoDB
+ 1 groupe d'utilisateurs Amazon Cognito
+ 2 groupes d'utilisateurs Amazon Cognito
+ 3 utilisateurs de groupes d'utilisateurs Amazon Cognito
+ 1 AWS AppSync API

À la fin du processus de création de la AWS CloudFormation pile, vous recevez un e-mail pour chacun des trois utilisateurs Amazon Cognito créés. Chaque e-mail contient un mot de passe temporaire que vous utilisez pour vous connecter à la console en tant qu'utilisateur Amazon Cognito. AWS AppSync Enregistrez les mots de passe pour le rappel du didacticiel.

## Configuration manuelle
<a name="manual-setup"></a>

Si vous préférez exécuter manuellement un step-by-step processus via la AWS AppSync console, suivez le processus de configuration ci-dessous.

### Configuration de vos AWS AppSync ressources non liées
<a name="setting-up-your-non-aws-appsync-resources"></a>

L'API communique avec deux tables DynamoDB : **une table d'images qui stocke des images et une** **table d'**amis qui stocke les relations entre les utilisateurs. L'API est configurée pour utiliser le groupe d'utilisateurs Amazon Cognito en tant que type d'authentification. La CloudFormation pile suivante configure ces ressources dans le compte.

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml)

À la fin du processus de création de la AWS CloudFormation pile, vous recevez un e-mail pour chacun des trois utilisateurs Amazon Cognito créés. Chaque e-mail contient un mot de passe temporaire à utiliser pour vous connecter en tant qu'utilisateur Amazon Cognito à la console AWS AppSync. Enregistrez les mots de passe pour le rappel du didacticiel.

### Création de votre API GraphQL
<a name="creating-your-graphql-api"></a>

Pour créer l'API GraphQL dans : AWS AppSync

1. Ouvrez la AWS AppSync console, choisissez **Build From Scratch**, puis **Start**.

1. Définissez le nom de l'API en spécifiant `AppSyncTutorial-PicturesViewer`.

1. Choisissez **Créer**.

La AWS AppSync console crée une nouvelle API GraphQL pour vous en utilisant le mode d'authentification par clé d'API. Vous pouvez utiliser la console pour configurer le reste de l'API GraphQL et exécuter des requêtes sur celle-ci jusqu'à la fin de ce didacticiel.

### Configuration de l'API GraphQL
<a name="configuring-the-graphql-api"></a>

Vous devez configurer l' AWS AppSync API avec le groupe d'utilisateurs Amazon Cognito que vous venez de créer.

1. Sélectionnez l’onglet **Paramètres**.

1. Dans la section **Authorization Type (Type d'autorisation)**, choisissez *Amazon Cognito User Pool (Groupe d'utilisateurs Amazon Cognito)*.

1. *Sous **Configuration du groupe d'utilisateurs**, choisissez **US-WEST-2** pour la région.AWS *

1. Choisissez le groupe UserPool d'utilisateurs **AppSyncTutorial-**.

1. Choisissez **DENY (REFUSER)** en tant que *Default Action (Action par défaut)*.

1. Laissez le champ **Regex du AppId client** vide.

1. Choisissez **Enregistrer**.

L'API est maintenant configurée pour utiliser le groupe d'utilisateurs Amazon Cognito en tant que type d'autorisation.

### Configuration des sources de données pour les tables DynamoDB
<a name="configuring-data-sources-for-the-ddb-tables"></a>

**Une fois les tables DynamoDB créées, accédez à votre API AWS AppSync GraphQL dans la console et choisissez l'onglet Sources de données.** Vous allez maintenant créer une source de données AWS AppSync pour chacune des tables DynamoDB que vous venez de créer.

1. Choisissez l'onglet **Source de données**.

1. Choisissez **New (Nouveau)** pour créer une nouvelle source de données.

1. Pour le nom de la source de données, saisissez `PicturesDynamoDBTable`.

1. Pour le type de source de données, choisissez **Table Amazon DynamoDB**.

1. Pour la région, choisissez **US-WEST-2**.

1. Dans la liste des tables, choisissez la table **AppSyncTutorial-Pictures DynamoDB**.

1. Dans la section **Créer ou utiliser un rôle existant**, choisissez **Rôle existant**.

1. Choisissez le rôle qui vient d'être créé à partir du CloudFormation modèle. Si vous n'avez pas modifié le *ResourceNamePrefix*, le nom du rôle doit être **AppSyncTutorial-Dynamo DBRole**.

1. Choisissez **Créer**.

Répétez le même processus pour la table des **amis**. Le nom de la table DynamoDB doit **AppSyncTutorialêtre** -Friends si vous n'avez pas modifié *ResourceNamePrefix*le paramètre au moment de créer la pile. CloudFormation 

### Création du schéma GraphQL
<a name="creating-the-graphql-schema"></a>

Maintenant que les sources de données sont connectées à vos tables DynamoDB, créons un schéma GraphQL. Dans l'éditeur de schéma de la AWS AppSync console, assurez-vous que votre schéma correspond au schéma suivant :

```
schema {
    query: Query
    mutation: Mutation
}

type Mutation {
    createPicture(input: CreatePictureInput!): Picture!
    @aws_auth(cognito_groups: ["Admins"])
    createFriendship(id: ID!, target: ID!): Boolean
    @aws_auth(cognito_groups: ["Admins"])
}

type Query {
    getPicturesByOwner(id: ID!): [Picture]
    @aws_auth(cognito_groups: ["Admins", "Viewers"])
}

type Picture {
    id: ID!
    owner: ID!
    src: String
}

input CreatePictureInput {
    owner: ID!
    src: String!
}
```

Choisissez **Save Schema (Enregistrer le schéma)** pour enregistrer votre schéma.

Certains des champs de schéma ont été annotés avec la directive *@aws\$1auth*. La configuration d'action par défaut de l'API étant définie sur *DENY (REFUSER)*, l'API rejette tous les utilisateurs qui ne sont pas membres des groupes mentionnés dans la directive *@aws\$1auth*. Pour plus d'informations sur la façon de sécuriser votre API, vous pouvez lire la page [Security (Sécurité)](security-authz.md#aws-appsync-security). *Dans ce cas, seuls les utilisateurs administrateurs ont accès aux champs *Mutation.CreatePicture et *Mutation.CreateFriendship**, tandis que les utilisateurs membres des groupes *Admins* ou Viewers peuvent accéder à la requête.* * getPicturesByChamp du propriétaire*. Tous les autres utilisateurs n'ont pas accès.

### Configuration des résolveurs
<a name="configuring-resolvers"></a>

Maintenant que vous avez un schéma GraphQL valide et deux sources de données, vous pouvez attacher des résolveurs aux champs GraphQL sur le schéma. L'&API propose les fonctions suivantes :
+ Créer une image via le champ *Mutation.createPicture*
+ Créer une relation via le champ *Mutation.createFriendship*
+ Extraire une image via le champ *Query.getPicture*

#### Mutation.createPicture
<a name="mutation-createpicture"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`createPicture(input: CreatePictureInput!): Picture!`. Choisissez la source de données *PicturesDynamoDBTable*DynamoDB. Dans la section **request mapping template (modèle de mappage de requête)**, ajoutez le modèle suivant :

```
#set($id = $util.autoId())

{
    "version" : "2018-05-29",

    "operation" : "PutItem",

    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($id),
        "owner": $util.dynamodb.toDynamoDBJson($ctx.args.input.owner)
    },

    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}
```

Dans la section **response mapping template (modèle de mappage de réponse)**, ajoutez le modèle suivant :

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($ctx.result)
```

La fonctionnalité de création d'image est créée. Vous enregistrez une image dans la table **Images**, en utilisant un UUID généré de façon aléatoire comme ID de l'image et en utilisant le nom d'utilisateur Cognito comme propriétaire de l'image.

#### Mutation.createFriendship
<a name="mutation-createfriendship"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`createFriendship(id: ID!, target: ID!): Boolean`. Choisissez la source de données **FriendsDynamoDBTable**DynamoDB. Dans la section **request mapping template (modèle de mappage de requête)**, ajoutez le modèle suivant :

```
#set($userToFriendFriendship = { "userId" : "$ctx.args.id", "friendId": "$ctx.args.target" })
#set($friendToUserFriendship = { "userId" : "$ctx.args.target", "friendId": "$ctx.args.id" })
#set($friendsItems = [$util.dynamodb.toMapValues($userToFriendFriendship), $util.dynamodb.toMapValues($friendToUserFriendship)])

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        ## Replace 'AppSyncTutorial-' default below with the ResourceNamePrefix you provided in the CloudFormation template
        "AppSyncTutorial-Friends": $util.toJson($friendsItems)
    }
}
```

Important : Dans le modèle de **BatchPutItem**demande, le nom exact de la table DynamoDB doit figurer. Le nom de table par défaut est *AppSyncTutorial-Friends.* Si vous utilisez le mauvais nom de table, un message d'erreur s'affiche lorsque AppSync vous essayez d'assumer le rôle fourni.

Pour simplifier ce didacticiel, procédez comme si la demande d'amitié avait été approuvée et enregistrez l'entrée de relation directement dans le **AppSyncTutorialFriends**tableau.

En effet, vous stockez deux éléments pour chaque amitié car la relation est bidirectionnelle. Pour plus d'informations sur les meilleures pratiques d'Amazon DynamoDB en matière de many-to-many représentation des relations, consultez la section Meilleures pratiques [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html).

Dans la section **response mapping template (modèle de mappage de réponse)**, ajoutez le modèle suivant :

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
true
```

Remarque : Assurez-vous que votre modèle de requête contient le bon nom de table. Le nom par défaut est *AppSyncTutorial-Friends*, mais le nom de votre table peut être différent si vous modifiez le CloudFormation **ResourceNamePrefix**paramètre.

#### Requête. getPicturesByPropriétaire
<a name="query-getpicturesbyowner"></a>

Maintenant que vous avez des relations d'amitié et des images, vous devez offrir aux utilisateurs la possibilité de voir les photos de leurs amis. Pour satisfaire à cette exigence, vous devez d'abord vérifier que le demandeur est ami avec le propriétaire, puis demander les images.

Cette fonctionnalité requiert deux opérations de source de données, vous allez donc créer deux fonctions. La première fonction, **isFriend**, vérifie si le demandeur et le propriétaire sont amis. La deuxième fonction, **getPicturesByOwner**, récupère les photos demandées à partir d'un identifiant de propriétaire. Examinons le flux d'exécution ci-dessous pour le résolveur proposé sur la *requête. getPicturesByChamp du propriétaire* :

1. Modèle de mappage Avant : préparer le contexte et les arguments d'entrée du champ.

1. Fonction isFriend : vérifie si le demandeur est le propriétaire de l'image. Dans le cas contraire, il vérifie si les utilisateurs demandeur et propriétaire sont amis en effectuant une opération GetItem DynamoDB sur la table des amis.

1. getPicturesBy*Fonction propriétaire : récupère les images de la table Pictures à l'aide d'une opération de requête DynamoDB sur l'index secondaire global owner-index.*

1. Modèle de mappage Après : mappe les résultats d'images pour que les attributs DynamoDB mappent correctement vers les champs de type GraphQL attendus.

Créez d'abord les fonctions.

##### Fonction isFriend
<a name="isfriend-function"></a>

1. Choisissez l'onglet **Functions (Fonctions)**.

1. Choisissez **Create Function (Créer une fonction)** pour créer une fonction.

1. Pour le nom de la source de données, saisissez `FriendsDynamoDBTable`.

1. Pour le nom de la fonction, saisissez *isFriend*.

1. Dans la zone de texte du modèle de mappage de requête, collez le modèle suivant :

   ```
   #set($ownerId = $ctx.prev.result.owner)
   #set($callerId = $ctx.prev.result.callerId)
   
   ## if the owner is the caller, no need to make the check
   #if($ownerId == $callerId)
       #return($ctx.prev.result)
   #end
   
   {
       "version" : "2018-05-29",
   
       "operation" : "GetItem",
   
       "key" : {
           "userId" : $util.dynamodb.toDynamoDBJson($callerId),
           "friendId" : $util.dynamodb.toDynamoDBJson($ownerId)
       }
   }
   ```

1. Dans la zone de texte du modèle de mappage de réponse, collez le modèle suivant :

   ```
   #if($ctx.error)
       $util.error("Unable to retrieve friend mapping message: ${ctx.error.message}", $ctx.error.type)
   #end
   
   ## if the users aren't friends
   #if(!$ctx.result)
       $util.unauthorized()
   #end
   
   $util.toJson($ctx.prev.result)
   ```

1. Choisissez **Créer une fonction**.

Résultat : vous avez créé la fonction **isFriend**.

##### getPicturesByFonction du propriétaire
<a name="getpicturesbyowner-function"></a>

1. Choisissez l'onglet **Functions (Fonctions)**.

1. Choisissez **Create Function (Créer une fonction)** pour créer une fonction.

1. Pour le nom de la source de données, saisissez `PicturesDynamoDBTable`.

1. Pour le nom de la fonction, saisissez `getPicturesByOwner`.

1. Dans la zone de texte du modèle de mappage de requête, collez le modèle suivant :

   ```
   {
       "version" : "2018-05-29",
   
       "operation" : "Query",
   
       "query" : {
           "expression": "#owner = :owner",
           "expressionNames": {
               "#owner" : "owner"
           },
           "expressionValues" : {
               ":owner" : $util.dynamodb.toDynamoDBJson($ctx.prev.result.owner)
           }
       },
   
       "index": "owner-index"
   }
   ```

1. Dans la zone de texte du modèle de mappage de réponse, collez le modèle suivant :

   ```
   #if($ctx.error)
       $util.error($ctx.error.message, $ctx.error.type)
   #end
   
   $util.toJson($ctx.result)
   ```

1. Choisissez **Créer une fonction**.

Résultat : vous avez créé la fonction **getPicturesByOwner**. Maintenant que les fonctions ont été créées, attachez un résolveur de pipeline à la *requête. getPicturesByChamp du propriétaire*.

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez **Attach Resolver** for`Query.getPicturesByOwner(id: ID!): [Picture]`. Sur la page suivante, choisissez le lien **Convert to pipeline resolver (Convertir en résolveur de pipeline)** qui s'affiche sous la source de données dans la liste déroulante. Utilisez ce qui suit pour le modèle de mappage Avant :

```
#set($result = { "owner": $ctx.args.id, "callerId": $ctx.identity.username })
$util.toJson($result)
```

Dans la section **after mapping template (modèle de mappage après)**, utilisez ce qui suit :

```
#foreach($picture in $ctx.result.items)
    ## prepend "src://" to picture.src property
    #set($picture['src'] = "src://${picture['src']}")
#end
$util.toJson($ctx.result.items)
```

Choisissez **Create Resolver (Créer un résolveur)**. Vous avez réussi à joindre votre première résolveur de pipeline. Sur la même page, ajoutez les deux fonctions que vous avez précédemment créées. Dans la section des fonctions, choisissez **Add A Function (Ajouter une fonction)**, puis choisissez ou saisissez le nom de la première fonction, **isFriend**. Ajoutez la deuxième fonction en suivant le même processus pour la fonction **getPicturesByOwner**. Assurez-vous que la fonction **IsFriend** apparaît en premier dans la liste, suivie de la fonction **getPicturesByOwner**. Vous pouvez utiliser les flèches vers le haut et vers le bas pour réorganiser l'ordre d'exécution des fonctions dans le pipeline.

Maintenant que le résolveur de pipeline est créé et que vous avez attaché les fonctions, nous allons tester l'API GraphQL nouvellement créée.

## Test de votre API GraphQL
<a name="testing-your-graphql-api"></a>

Tout d'abord, vous devez remplir les images et les relations d'amitié en exécutant quelques mutations à l'aide de l'utilisateur administrateur que vous avez créé. Sur le côté gauche de la AWS AppSync console, choisissez l'onglet **Requêtes**.

### createPicture Mutation
<a name="createpicture-mutation"></a>

1. Dans AWS AppSync la console, choisissez l'onglet **Requêtes**.

1. Choisissez **Login With User Pools (Connexion avec les groupes d'utilisateur)**.

1. Dans le modal, entrez l'ID de client d'exemple Cognito créé par la CloudFormation pile (par exemple, 37solo6mmhh7k4v63cqdfgdg5d).

1. Entrez le nom d'utilisateur que vous avez passé en paramètre à la CloudFormation pile. La valeur par défaut est **nadia**.

1. Utilisez le mot de passe temporaire qui a été envoyé à l'e-mail que vous avez fourni en tant que paramètre à la CloudFormation pile (par exemple, *UserPoolUserEmail*).

1. Choisissez **Login (Connexion)**. Vous devriez maintenant voir le bouton renommé **Logout nadia**, ou le nom d'utilisateur que vous avez choisi lors de la création de la CloudFormation pile (c'est-à-dire, *UserPoolUsername*).

Nous allons envoyer quelques mutations *createPicture* pour remplir la table d'images. Exécutez la requête GraphQL suivante dans la console :

```
mutation {
  createPicture(input:{
    owner: "nadia"
    src: "nadia.jpg"
  }) {
    id
    owner
    src
  }
}
```

La réponse doit être similaire à ce qui suit :

```
{
  "data": {
    "createPicture": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "owner": "nadia",
      "src": "nadia.jpg"
    }
  }
}
```

Ajoutons quelques images de plus :

```
mutation {
  createPicture(input:{
    owner: "shaggy"
    src: "shaggy.jpg"
  }) {
    id
    owner
    src
  }
}
```

```
mutation {
  createPicture(input:{
    owner: "rex"
    src: "rex.jpg"
  }) {
    id
    owner
    src
  }
}
```

Vous avez ajouté trois images en utilisant **nadia** en tant qu'utilisateur administrateur.

### Mutation createFriendship
<a name="createfriendship-mutation"></a>

Nous allons ajouter une entrée d'amitié. Exécutez les mutations suivantes dans la console.

Remarque : vous devez être connecté en tant qu'utilisateur administrateur (l'utilisateur administrateur par défaut est **nadia**).

```
mutation {
  createFriendship(id: "nadia", target: "shaggy")
}
```

La réponse doit être similaire à ce qui suit :

```
{
  "data": {
    "createFriendship": true
  }
}
```

 **nadia** et **shaggy** sont amis. **rex** n'est ami avec personne.

### getPicturesByRequête du propriétaire
<a name="getpicturesbyowner-query"></a>

Pour cette étape, connectez-vous en tant qu'utilisateur **nadia** à l'aide des groupes d'utilisateurs Cognito, en utilisant les informations d'identification définies au début de ce didacticiel. Comme **nadia**, extrayez les images détenues par **shaggy**.

```
query {
    getPicturesByOwner(id: "shaggy") {
        id
        owner
        src
    }
}
```

Comme **nadia** et **shaggy** sont amis, la requête devrait renvoyer l'image adéquate.

```
{
  "data": {
    "getPicturesByOwner": [
      {
        "id": "05a16fba-cc29-41ee-a8d5-4e791f4f1079",
        "owner": "shaggy",
        "src": "src://shaggy.jpg"
      }
    ]
  }
}
```

De même, si **nadia** essaie d'extraire ses propres images,elle réussira. Le résolveur de pipeline a été optimisé pour éviter d'exécuter l'opération ** GetItem IsFriend** dans ce cas. Essayez la requête suivante :

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Si vous activez la journalisation sur votre API (dans le volet **Settings (Paramètres)**, configurez le niveau de débogage comme **ALL (TOUS)**, et exécutez à nouveau la même requête. Elle renvoie des journaux pour l'exécution du champ. En examinant les journaux, vous pouvez déterminer si la fonction **isFriend** a été renvoyée de façon précoce lors de l'étape **Modèle de mappage de requête** :

```
{
  "errors": [],
  "mappingTemplateType": "Request Mapping",
  "path": "[getPicturesByOwner]",
  "resolverArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/types/Query/fields/getPicturesByOwner",
  "functionArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/functions/o2f42p2jrfdl3dw7s6xub2csdfs",
  "functionName": "isFriend",
  "earlyReturnedValue": {
    "owner": "nadia",
    "callerId": "nadia"
  },
  "context": {
    "arguments": {
      "id": "nadia"
    },
    "prev": {
      "result": {
        "owner": "nadia",
        "callerId": "nadia"
      }
    },
    "stash": {},
    "outErrors": []
  },
  "fieldInError": false
}
```

La *earlyReturnedValue*clé représente les données renvoyées par la directive *\$1return*.

**Enfin, même si **Rex** est membre du **Viewers** Cognito UserPool Group, et parce que **Rex** n'est ami avec personne, il ne pourra accéder à aucune des photos détenues par **Shaggy** ou Nadia.** Si vous vous connectez en tant que **rex** dans la console et exécutez la requête suivante :

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Vous obtenez l'erreur de non autorisation suivante :

```
{
  "data": {
    "getPicturesByOwner": null
  },
  "errors": [
    {
      "path": [
        "getPicturesByOwner"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 9,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access getPicturesByOwner on type Query"
    }
  ]
}
```

Vous avez réussi à mettre en place une autorisation complexe à l'aide de résolveurs de pipeline.

# Utilisation des opérations Delta Sync sur des sources de données versionnées dans AWS AppSync
<a name="tutorial-delta-sync"></a>

**Note**  
Nous prenons désormais principalement en charge le runtime APPSYNC\$1JS et sa documentation. [Pensez à utiliser le runtime APPSYNC\$1JS et ses guides ici.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Les applications clientes AWS AppSync stockent les données en mettant en cache les réponses GraphQL localement sur le disque d'une application. mobile/web Les sources de données et les opérations `Sync` versionnées permettent aux clients d'effectuer le processus de synchronisation à l'aide d'un seul résolveur. Cela permet aux clients de remplir leur cache local avec les résultats d'une requête de base pouvant comprendre un grand nombre d'enregistrements, puis de recevoir uniquement les données modifiées depuis leur dernière requête (les *mises à jour delta*). En permettant aux clients de séparer l'hydratation de base du cache avec une demande initiale et les mises à jour incrémentielles d'une autre demande, vous pouvez déplacer le calcul de votre application cliente vers le backend. Ceci est nettement plus efficace pour les applications clientes qui basculent fréquemment entre les états en ligne et hors ligne.

Pour implémenter Delta Sync, la requête `Sync` utilise l'opération `Sync` sur une source de données versionnée. Lorsqu'une AWS AppSync mutation modifie un élément dans une source de données versionnée, un enregistrement de cette modification est également stocké dans la table *Delta*. Vous pouvez choisir d'utiliser différentes tables *Delta* (par exemple, une par type, une par domaine) pour les autres sources de données versionnées ou une seule table *Delta* pour votre API. AWS AppSync recommande de ne pas utiliser une seule table *Delta* pour plusieurs APIs afin d'éviter la collision des clés primaires.

En outre, les clients Delta Sync peuvent également recevoir un abonnement en tant qu'argument, puis les coordonne l'abonnement et écrit entre les transitions en lige et hors ligne. Delta Sync effectue cette opération en relançant automatiquement les abonnements, notamment le backoff exponentiel et réessaie avec une instabilité via différents scénarios d'erreur et le stockage des événements dans une file d'attente. La requête de base ou delta appropriée est ensuite exécutée avant de fusionner des événements de la file d'attente, puis de traiter les abonnements normalement.

La documentation des options de configuration du client, y compris l'Amplify DataStore, est disponible sur le site Web [Amplify](https://aws-amplify.github.io/) Framework. Cette documentation explique comment configurer les sources de données DynamoDB versionnées et les opérations `Sync` pour qu'elles fonctionnent avec le client Delta Sync pour un accès optimal aux données.

## Configuration en un clic
<a name="one-click-setup"></a>

Pour configurer automatiquement le point de terminaison GraphQL AWS AppSync avec tous les résolveurs configurés et les AWS ressources nécessaires, utilisez ce modèle : AWS CloudFormation 

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/deltasync/deltasync-v2-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/deltasync/deltasync-v2-full.yaml) 

Cette pile crée les ressources suivantes dans votre compte :
+ 2 tables DynamoDB (Base et Delta)
+ 1 AWS AppSync API avec clé d'API
+ 1 rôle IAM avec politique pour les tables DynamoDB

Deux tables sont utilisées pour partitionner vos requêtes de synchronisation dans une seconde table agissant comme un journal d'événements manqués lorsque les clients étaient hors ligne. Pour garantir l'efficacité des requêtes sur la table delta, [Amazon TTLs DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) est utilisé pour organiser automatiquement les événements selon les besoins. L'heure TTL est configurable pour vos besoins sur la source de données (vous pouvez définir 1 heure, 1 jour, etc.).

## Schema
<a name="schema"></a>

Pour illustrer Delta Sync, l'exemple d'application crée un schéma *Posts* basé sur une table *Base* et *Delta* dans DynamoDB. AWS AppSync écrit automatiquement les mutations dans les deux tables. La requête de synchronisation tire des enregistrements des tables de *Base* ou *Delta* selon les besoins, et un seul abonnement est défini pour montrer comment les clients peuvent en tirer parti dans leur logique de reconnexion.

```
input CreatePostInput {
    author: String!
    title: String!
    content: String!
    url: String
    ups: Int
    downs: Int
    _version: Int
}

interface Connection {
  nextToken: String
  startedAt: AWSTimestamp!
}

type Mutation {
    createPost(input: CreatePostInput!): Post
    updatePost(input: UpdatePostInput!): Post
    deletePost(input: DeletePostInput!): Post
}

type Post {
    id: ID!
    author: String!
    title: String!
    content: String!
    url: AWSURL
    ups: Int
    downs: Int
    _version: Int
    _deleted: Boolean
    _lastChangedAt: AWSTimestamp!
}

type PostConnection implements Connection {
    items: [Post!]!
    nextToken: String
    startedAt: AWSTimestamp!
}

type Query {
    getPost(id: ID!): Post
    syncPosts(limit: Int, nextToken: String, lastSync: AWSTimestamp): PostConnection!
}

type Subscription {
    onCreatePost: Post
        @aws_subscribe(mutations: ["createPost"])
    onUpdatePost: Post
        @aws_subscribe(mutations: ["updatePost"])
    onDeletePost: Post
        @aws_subscribe(mutations: ["deletePost"])
}

input DeletePostInput {
    id: ID!
    _version: Int!
}

input UpdatePostInput {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    _version: Int!
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}
```

Le schéma GraphQL est standard, mais il peut être nécessaire d'appeler avant de continuer, pour deux raisons. Tout d'abord, toutes les mutations écrivent automatiquement dans la table *Base* en premier lieu, puis dans la table *Delta*. La table de *Base* est la source de vérité centrale pour l'état tandis que la table *Delta* est votre journal. Si vous ne passez pas dans la `lastSync: AWSTimestamp`, la requête `syncPosts` s'exécute sur la table de *Base* et hydrate le cache tout en s'exécutant périodiquement en tant que *processus de rattrapage global* pour les cas limites où les clients sont hors ligne plus longtemps que le temps TTL que vous aviez configuré dans la table *Delta*. Si vous passez dans le `lastSync: AWSTimestamp`, la requête `syncPosts` s'exécute sur votre table *Delta* et est utilisée par les clients pour récupérer des événements modifiés la dernière fois qu'ils étaient hors ligne. Les clients Amplify transmettent automatiquement la valeur `lastSync: AWSTimestamp` et persistent sur le disque de manière appropriée.

Le champ *\$1deleted* sur *Post* est utilisé pour **SUPPRIMER** des opérations. Lorsque des clients sont hors ligne et que des enregistrements sont supprimés de la table *Base*, cet attribut informe les clients effectuant la synchronisation d'expulser les éléments de leur cache local. Si des clients sont hors ligne pour de plus longues périodes et que l'élément a été supprimé avant que le client ne récupère cette valeur avec une requête Delta Sync, l'événement de rattrapage global dans la requête de base (configurable dans le client), s'exécute et supprime l'élément à partir du cache. Ce champ est marqué comme facultatif car il ne renvoie de valeur que lors de l'exécution d'une requête de synchronisation ayant supprimé les objets présents.

## Mutations
<a name="mutations"></a>

Pour toutes les mutations, AWS AppSync effectue une Create/Update/Delete opération standard dans la table *de base* et enregistre également automatiquement la modification dans la table *Delta*. Vous pouvez réduire ou prolonger la période de conservation des enregistrements en modifiant la valeur `DeltaSyncTableTTL` sur la source de données. Pour les organisations avec une vitesse de données élevée, il est recommandé de la garder courte. Si vos clients sont hors ligne plus longtemps, il peut être prudent de la garder plus longue.

## Requêtes de synchronisation
<a name="sync-queries"></a>

La *requête de base* est une opération DynamoDB Sync sans `lastSync` valeur spécifiée. Cela fonctionne pour de nombreuses organisations car la requête de base ne s'exécute qu'au démarrage et par la suite, à intervalles réguliers.

La *requête delta* est une opération de synchronisation DynamoDB avec `lastSync` une valeur spécifiée. La *requête delta* s'exécute chaque fois que le client se reconnecte alors qu'il était hors ligne (à condition que le temps périodique de requête de base n'ait pas déclenchée l'exécution). Les clients suivent automatiquement la dernière fois qu'ils ont exécuté une requête pour synchroniser les données.

Lorsqu'une requête delta est exécutée, le résolveur de la requête utilise `ds_pk` et `ds_sk` pour interroger uniquement les enregistrements qui ont changé depuis la dernière synchronisation par le client. Le client stocke la réponse GraphQL appropriée.

Pour de plus amples informations sur l'exécution des requêtes de synchronisation, veuillez consulter la [documentation Opération de synchronisation](aws-appsync-conflict-detection-and-sync-sync-operations.md).

## Exemple
<a name="example"></a>

Commençons d'abord par appeler une mutation `createPost` pour créer un élément :

```
mutation create {
  createPost(input: {author: "Nadia", title: "My First Post", content: "Hello World"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

La valeur de retour de cette mutation sera la suivante :

```
{
  "data": {
    "createPost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "My First Post",
      "content": "Hello World",
      "_version": 1,
      "_lastChangedAt": 1574469356331,
      "_deleted": null
    }
  }
}
```

Si vous examinez le contenu de la table *Base*, vous verrez un enregistrement qui ressemble à :

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Si vous examinez le contenu de la table *Delta*, vous verrez un enregistrement qui ressemble à :

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_ttl": {
    "N": "1574472956"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:35:56.331:81d36bbb-1579-4efe-92b8-2e3f679f628b:1"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Maintenant, nous pouvons simuler une requête *de base* qu'un client va exécuter pour hydrater son magasin de données local en utilisant une requête `syncPosts` comme :

```
query baseQuery {
  syncPosts(limit: 100, lastSync: null, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
      _lastChangedAt
    }
    startedAt
    nextToken
  }
}
```

La valeur de retour de cette requête *de base* sera la suivante :

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "My First Post",
          "content": "Hello World",
          "_version": 1,
          "_lastChangedAt": 1574469356331
        }
      ],
      "startedAt": 1574469602238,
      "nextToken": null
    }
  }
}
```

Nous enregistrerons la valeur `startedAt` plus tard pour simuler une requête *Delta* mais nous devons d'abord apporter une modification à notre table. Utilisons la mutation `updatePost` pour modifier notre publication existante :

```
mutation updatePost {
  updatePost(input: {id: "81d36bbb-1579-4efe-92b8-2e3f679f628b", _version: 1, title: "Actually this is my Second Post"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

La valeur de retour de cette mutation sera la suivante :

```
{
  "data": {
    "updatePost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "Actually this is my Second Post",
      "content": "Hello World",
      "_version": 2,
      "_lastChangedAt": 1574469851417,
      "_deleted": null
    }
  }
}
```

Si vous examinez maintenant le contenu de la table *Base* vous devriez voir l'élément mis à jour :

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Si vous examinez maintenant le contenu de la table *Delta*, vous devriez voir deux enregistrements :

1. Enregistrement lors de la création de l'élément

1. Enregistrement de la date à laquelle l'élément a été mis à jour.

Le nouvel élément ressemblera à :

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_ttl": {
    "N": "1574473451"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:44:11.417:81d36bbb-1579-4efe-92b8-2e3f679f628b:2"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Maintenant, nous pouvons simuler une requête *Delta* pour récupérer les modifications qui se sont produites lorsqu'un client était hors ligne. Nous utiliserons la valeur `startedAt` renvoyée par notre requête de *Base* pour effectuer la requête :

```
query delta {
  syncPosts(limit: 100, lastSync: 1574469602238, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
    }
    startedAt
    nextToken
  }
}
```

La valeur de retour de cette requête *Delta* sera la suivante :

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "Actually this is my Second Post",
          "content": "Hello World",
          "_version": 2
        }
      ],
      "startedAt": 1574470400808,
      "nextToken": null
    }
  }
}
```