

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.

# JavaScript tutoriels de résolution pour AWS AppSync
<a name="tutorials-js"></a>

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.

**Topics**
+ [Création d'une post-application simple à l'aide des résolveurs DynamoDB JavaScript](tutorial-dynamodb-resolvers-js.md)
+ [Utiliser des AWS Lambda résolveurs](tutorial-lambda-resolvers-js.md)
+ [Utilisation de résolveurs locaux](tutorial-local-resolvers-js.md)
+ [Combinaison de résolveurs GraphQL](tutorial-combining-graphql-resolvers-js.md)
+ [Utilisation des résolveurs OpenSearch de service](tutorial-elasticsearch-resolvers-js.md)
+ [Exécution de transactions DynamoDB](tutorial-dynamodb-transact-js.md)
+ [Utilisation des opérations par lots DynamoDB](tutorial-dynamodb-batch-js.md)
+ [Utilisation de résolveurs HTTP](tutorial-http-resolvers-js.md)
+ [Utilisation d'Aurora PostgreSQL avec une API de données](aurora-serverless-tutorial-js.md)

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

Dans ce didacticiel, vous allez importer vos tables Amazon DynamoDB et les connecter AWS AppSync pour créer une API GraphQL entièrement fonctionnelle à l' JavaScript aide de résolveurs de pipeline que vous pouvez exploiter dans votre propre application.

Vous utiliserez la AWS AppSync console pour approvisionner vos ressources Amazon DynamoDB, créer vos résolveurs et les connecter à vos sources de données. Vous pourrez également lire et écrire dans votre base de données Amazon DynamoDB via des instructions GraphQL et vous abonner à des données en temps réel.

Certaines étapes spécifiques doivent être effectuées pour que les instructions GraphQL soient traduites en opérations Amazon DynamoDB et pour que les réponses soient retraduites 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.

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

**Pour créer une API GraphQL dans AWS AppSync**

1. Ouvrez la AppSync console et choisissez **Create API**.

1. Sélectionnez **Design from scratch** et choisissez **Next**.

1. Donnez un nom à votre API`PostTutorialAPI`, puis choisissez **Next**. Passez à la page de révision tout en conservant les valeurs par défaut pour les autres options, puis choisissez`Create`.

La AWS AppSync console crée une nouvelle API GraphQL pour vous. Par défaut, il utilise le mode d'authentification par clé 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="define-post-api"></a>

Maintenant que vous disposez de votre API 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.

**Pour ajouter des données à votre schéma**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Nous allons créer un schéma qui définit un `Post` type et une opération `addPost` pour ajouter et obtenir `Post` des objets. 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 **Sauvegarder schéma**.

## Configuration de votre table Amazon DynamoDB
<a name="configure-dynamodb"></a>

La AWS AppSync console peut vous aider à fournir les AWS ressources nécessaires pour stocker vos propres ressources dans une table Amazon DynamoDB. Au cours de cette étape, vous allez créer une table Amazon DynamoDB pour stocker vos publications. Vous allez également configurer un [index secondaire](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html) que nous utiliserons ultérieurement.

**Pour créer votre table Amazon DynamoDB**

1. Sur la page **Schéma**, choisissez **Create Resources**.

1. Choisissez **Utiliser le type existant**, puis choisissez le `Post` type.

1. Dans la section **Index supplémentaires**, choisissez **Ajouter un index**.

1. Donnez un nom à l'index`author-index`.

1. Réglez le `Primary key` point sur `author` et la `Sort` touche sur`None`.

1. Désactivez **Générer automatiquement GraphQL**. Dans cet exemple, nous allons créer le résolveur nous-mêmes.

1. Choisissez **Créer**.

Vous avez maintenant une nouvelle source de données appelée`PostTable`, que vous pouvez consulter en accédant à **Sources de données** dans l'onglet latéral. Vous utiliserez cette source de données pour lier vos requêtes et mutations à votre table Amazon DynamoDB. 

## Configuration d'un résolveur AddPost (Amazon DynamoDB) PutItem
<a name="configure-addpost"></a>

Maintenant AWS AppSync que vous connaissez la table Amazon 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 résolveur de `addPost` pipeline utilisé JavaScript, qui vous permet de créer une publication dans votre table Amazon DynamoDB. Un résolveur de pipeline comporte les composants suivants : 
+ L'emplacement dans le schéma GraphQL pour joindre le résolveur. Dans ce cas, vous configurez un résolveur sur le champ `createPost` sur le type `Mutation`. Ce résolveur sera invoqué lorsque l'appelant appellera une mutation. `{ addPost(...){...} }` 
+ La source de données à utiliser pour ce résolveur. Dans ce cas, vous souhaitez utiliser la source de données DynamoDB que vous avez définie précédemment afin de pouvoir ajouter des entrées dans la `post-table-for-tutorial` table DynamoDB.
+ Le gestionnaire de demandes. Le gestionnaire de demandes est une fonction qui gère la demande entrante de l'appelant et la traduit en instructions AWS AppSync à exécuter par rapport à DynamoDB.
+ Le gestionnaire de réponses. Le rôle du gestionnaire de réponse est de gérer la réponse de DynamoDB et de la retraduire en une réponse attendue par GraphQL. 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 votre résolveur**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Dans le volet **Résolveurs**, recherchez le `addPost` champ situé sous le `Mutation` type, puis choisissez **Attacher**.

1. Choisissez votre source de données, puis sélectionnez **Créer**.

1. Dans votre éditeur de code, remplacez le code par cet extrait :

   ```
   import { util } from '@aws-appsync/utils'
   import * as ddb from '@aws-appsync/utils/dynamodb'
   
   export function request(ctx) {
   	const item = { ...ctx.arguments, ups: 1, downs: 0, version: 1 }
   	const key = { id: ctx.args.id ?? util.autoId() }
   	return ddb.put({ key, item })
   }
   
   export function response(ctx) {
   	return ctx.result
   }
   ```

1. Choisissez **Enregistrer**.

**Note**  
Dans ce code, vous utilisez les utilitaires du module DynamoDB qui vous permettent de créer facilement des requêtes DynamoDB.

AWS AppSync est livré avec un utilitaire de génération automatique d'identifiant appelé`util.autoId()`, qui est utilisé pour générer un identifiant pour votre nouveau message. Si vous ne spécifiez pas d'identifiant, l'utilitaire le générera automatiquement pour vous.

```
const key = { id: ctx.args.id ?? util.autoId() }
```

Pour plus d'informations sur les utilitaires disponibles pour JavaScript, consultez les [fonctionnalités JavaScript d'exécution pour les résolveurs et les fonctions](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html). 

### Appelez l'API pour ajouter une publication
<a name="call-api-addpost"></a>

Maintenant que le résolveur a été configuré, il AWS AppSync peut traduire une `addPost` mutation entrante en une opération Amazon DynamoDB`PutItem`. Vous pouvez désormais exécuter une mutation pour placer quelque chose dans la table.

**Pour exécuter l'opération**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante :

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

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`addPost`. Les résultats de la publication nouvellement créée doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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
       }
     }
   }
   ```

L'explication suivante montre ce qui s'est passé :

1. AWS AppSync a reçu une demande de `addPost` mutation.

1. AWS AppSync exécute le gestionnaire de requêtes du résolveur. La `ddb.put` fonction crée une `PutItem` demande qui ressemble à ceci :

   ```
   {
     operation: 'PutItem',
     key: { id: { S: '123' } },
     attributeValues: {
       downs: { N: 0 },
       author: { S: 'AUTHORNAME' },
       ups: { N: 1 },
       title: { S: 'Our first post!' },
       version: { N: 1 },
       content: { S: 'This is our first post.' },
       url: { S: 'https://aws.amazon.com/appsync/' }
     }
   }
   ```

1. AWS AppSync utilise cette valeur pour générer et exécuter une demande Amazon `PutItem` DynamoDB.

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

1. Le gestionnaire de réponses renvoie le résultat immédiatement (`return ctx.result`).

1. Le résultat final est visible dans la réponse GraphQL.

## Configuration du résolveur GetPost (Amazon DynamoDB) GetItem
<a name="configure-getpost"></a>

Maintenant que vous pouvez ajouter des données à la table Amazon DynamoDB, vous devez configurer `getPost` la requête afin qu'elle puisse extraire ces données de la table. Pour ce faire, vous configurez un autre résolveur.

**Pour ajouter votre résolveur**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Dans le volet **Resolvers** sur la droite, recherchez le `getPost` champ correspondant au `Query` type, puis choisissez **Attacher**.

1. Choisissez votre source de données, puis sélectionnez **Créer**.

1. Dans l'éditeur de code, remplacez le code par cet extrait :

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb'
   	
   export function request(ctx) {
   	return ddb.get({ key: { id: ctx.args.id } })
   }
   
   export const response = (ctx) => ctx.result
   ```

1. Enregistrez votre résolveur.

**Note**  
Dans ce résolveur, nous utilisons une expression de fonction flèche pour le gestionnaire de réponses.

### Appelez l'API pour obtenir un message
<a name="call-api-getpost"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `getPost` requête entrante en une opération Amazon 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.

**Pour exécuter votre requête**

1. Dans votre API, choisissez l'onglet **Requêtes**. 

1. Dans le volet **Requêtes**, ajoutez le code suivant et utilisez l'identifiant que vous avez copié après avoir créé votre publication :

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

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`getPost`. Les résultats de la publication nouvellement créée doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**.

1. **La publication extraite d'Amazon DynamoDB doit apparaître dans **le** volet Résultats à droite du volet 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
       }
     }
   }
   ```

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 fonction de demande 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, la fonction de requête peut ressembler à l'extrait ci-dessous :

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	return ddb.get({
		key: { id: ctx.args.id },
		projection: ['author', 'id', 'title'],
	})
}

export const response = (ctx) => ctx.result
```

Vous pouvez également utiliser un [selectionSetList](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html#aws-appsync-resolver-context-reference-info-js)signe avec `getPost` pour représenter `expression` :

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	const projection = ctx.info.selectionSetList.map((field) => field.replace('/', '.'))
	return ddb.get({ key: { id: ctx.args.id }, projection })
}

export const response = (ctx) => ctx.result
```

## Création d'une mutation UpdatePost (Amazon DynamoDB) UpdateItem
<a name="configure-updatepost"></a>

Jusqu'à présent, vous pouvez créer et récupérer `Post` des objets dans Amazon DynamoDB. Vous allez ensuite configurer une nouvelle mutation pour mettre à jour un objet. Comparée à la `addPost` mutation qui nécessite que tous les champs soient spécifiés, cette mutation vous permet de spécifier uniquement les champs que vous souhaitez modifier. Il a également introduit un nouvel `expectedVersion` argument qui vous permet de spécifier la version que vous souhaitez modifier. Vous allez définir une condition garantissant que vous modifiez la dernière version de l'objet. Pour ce faire, utilisez l'opération `UpdateItem` Amazon DynamoDB .sc

**Pour mettre à jour votre résolveur**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. 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,
           expectedVersion: Int!
       ): Post
       
       addPost(
           id: ID
           author: String!
           title: String!
           content: String!
           url: String!
       ): Post!
   }
   ```

1. Choisissez **Sauvegarder schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `updatePost` champ nouvellement créé sur le `Mutation` type, puis choisissez **Joindre**. Créez votre nouveau résolveur à l'aide de l'extrait ci-dessous :

   ```
   import { util } from '@aws-appsync/utils';
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { id, expectedVersion, ...rest } = ctx.args;
     const values = Object.entries(rest).reduce((obj, [key, value]) => {
       obj[key] = value ?? ddb.operations.remove();
       return obj;
     }, {});
   
     return ddb.update({
       key: { id },
       condition: { version: { eq: expectedVersion } },
       update: { ...values, version: ddb.operations.increment(1) },
     });
   }
   
   export function response(ctx) {
     const { error, result } = ctx;
     if (error) {
       util.appendError(error.message, error.type);
     }
     return result;
   ```

1. Enregistrez toutes les modifications que vous avez apportées.

Ce résolveur permet `ddb.update` de créer une demande Amazon DynamoDB`UpdateItem`. Au lieu de rédiger l'article dans son intégralité, vous demandez simplement à Amazon DynamoDB de mettre à jour certains attributs. Cela se fait à l'aide des expressions de mise à jour Amazon DynamoDB.

La `ddb.update` fonction prend une clé et un objet de mise à jour comme arguments. Ensuite, vous vérifiez les valeurs des arguments entrants. Lorsqu'une valeur est définie sur`null`, utilisez l'opération `remove` DynamoDB pour signaler que la valeur doit être supprimée de l'élément DynamoDB.

Il y a également une nouvelle `condition` section. Une expression de condition vous permet de dire AWS AppSync à Amazon DynamoDB si la demande doit aboutir ou non en fonction de l'état de l'objet déjà présent dans Amazon DynamoDB avant que l'opération ne soit effectuée. Dans ce cas, vous souhaitez que la `UpdateItem` demande aboutisse uniquement si le `version` champ de l'article actuellement dans Amazon DynamoDB correspond `expectedVersion` exactement à l'argument. Lorsque l'élément est mis à jour, nous voulons augmenter la valeur du`version`. C'est facile à faire avec la fonction d'opération`increment`.

Pour plus d'informations sur les expressions de condition, consultez la documentation sur les [expressions de condition](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-condition-expressions).

Pour plus d'informations sur la `UpdateItem` demande, consultez la [UpdateItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-updateitem)documentation et celle du [module DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/built-in-modules-js.html). 

Pour plus d'informations sur la façon d'écrire des expressions de mise à jour, consultez la documentation [ UpdateExpressionsDynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

### Appelez l'API pour mettre à jour une publication
<a name="call-api-updatepost"></a>

Essayons de mettre à jour l'`Post`objet avec le nouveau résolveur.

**Pour mettre à jour votre objet**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante. Vous devrez également mettre à jour l'`id`argument avec la valeur que vous avez notée précédemment :

   ```
   mutation updatePost {
     updatePost(
       id:123
       title: "An empty story"
       content: null
       expectedVersion: 1
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`updatePost`.

1. **La publication mise à jour dans Amazon DynamoDB doit apparaître dans **le** volet Résultats à droite du volet 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": 2
       }
     }
   }
   ```

Dans cette demande, vous avez demandé à Amazon DynamoDB de mettre à jour uniquement `title` les `content` champs AWS AppSync et. Tous les autres champs ont été laissés de côté (à l'exception de l'incrémentation du `version` champ). Vous avez défini une nouvelle valeur pour `title` l'attribut et vous l'`content`avez supprimé de la publication. Les champs `author`, `url`, `ups` et `downs` sont restés inchangés. Réessayez d'exécuter la demande de mutation en la laissant exactement telle quelle. La réponse devrait être similaire à ce qui suit :

```
{
  "data": {
    "updatePost": null
  },
  "errors": [
    {
      "path": [
        "updatePost"
      ],
      "data": null,
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 1RR3QN5F35CS8IV5VR4OQO9NNBVV4KQNSO5AEMVJF66Q9ASUAAJG)"
    }
  ]
}
```

La demande échoue car l'expression de condition est évaluée à `false` : 

1. La première fois que vous avez exécuté la demande, la valeur du `version` champ de la publication dans Amazon DynamoDB `1` était, ce qui correspondait à l'argument. `expectedVersion` La demande a abouti, ce qui signifie que le `version` champ a été incrémenté dans Amazon DynamoDB à. `2`

1. La deuxième fois que vous avez exécuté la demande, la valeur du `version` champ de la publication dans Amazon DynamoDB `2` était, ce qui ne correspondait pas à l'argument. `expectedVersion`

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

## Création de mutations de vote (Amazon DynamoDB UpdateItem)
<a name="configure-vote-mutations"></a>

Le `Post` type contient `ups` des `downs` champs permettant l'enregistrement des votes positifs et négatifs. Cependant, pour le moment, l'API ne nous permet pas de faire quoi que ce soit avec eux. Ajoutons une mutation pour nous permettre de voter pour et contre les publications.

**Pour ajouter votre mutation**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Dans le volet **Schéma**, modifiez le `Mutation` type et ajoutez l'`DIRECTION`énumération pour ajouter de nouvelles mutations de vote :

   ```
   type Mutation {
       vote(id: ID!, direction: DIRECTION!): Post
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       addPost(
           id: ID,
           author: String!,
           title: String!,
           content: String!,
           url: String!
       ): Post!
   }
   
   enum DIRECTION {
     UP
     DOWN
   }
   ```

1. Choisissez **Sauvegarder schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `vote` champ nouvellement créé sur le `Mutation` type, puis choisissez **Joindre**. Créez un nouveau résolveur en créant et en remplaçant le code par l'extrait suivant :

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const field = ctx.args.direction === 'UP' ? 'ups' : 'downs';
     return ddb.update({
       key: { id: ctx.args.id },
       update: {
         [field]: ddb.operations.increment(1),
         version: ddb.operations.increment(1),
       },
     });
   }
   
   export const response = (ctx) => ctx.result;
   ```

1. Enregistrez toutes les modifications que vous avez apportées.

### Appelez l'API pour voter pour ou contre une publication
<a name="call-api-vote"></a>

Maintenant que les nouveaux résolveurs ont été configurés, AWS AppSync il sait comment traduire une entrée `upvotePost` ou une `downvote` mutation en une opération Amazon 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.

**Pour exécuter votre mutation**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante. Vous devrez également mettre à jour l'`id`argument avec la valeur que vous avez notée précédemment :

   ```
   mutation votePost {
     vote(id:123, direction: UP) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`votePost`.

1. **La publication mise à jour dans Amazon DynamoDB doit apparaître dans **le** volet Résultats à droite du volet Requêtes.** Il doit ressembler à l’exemple ci-dessous.

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

1. Choisissez **Exécuter** quelques fois de plus. Vous devriez voir les `version` champs `ups` et augmenter à `1` chaque fois que vous exécutez la requête.

1. Modifiez la requête pour l'appeler avec un nom différent`DIRECTION`.

   ```
   mutation votePost {
     vote(id:123, direction: DOWN) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`votePost`.

   Cette fois, vous devriez voir les `version` champs `downs` et augmenter à `1` chaque fois que vous exécutez la requête.

## Configuration d'un résolveur DeletePost (Amazon DynamoDB) DeleteItem
<a name="configure-deletepost"></a>

Ensuite, vous devez créer une mutation pour supprimer une publication. Pour ce faire, utilisez l'opération `DeleteItem` Amazon DynamoDB.

**Pour ajouter votre mutation**

1. Dans votre schéma, choisissez l'onglet **Schéma**.

1. Dans le volet **Schéma**, modifiez le `Mutation` type pour ajouter une nouvelle `deletePost` mutation :

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

1. Cette fois, vous avez rendu le `expectedVersion` champ facultatif. Ensuite, choisissez **Enregistrer le schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `delete` champ nouvellement créé dans le `Mutation` type, puis choisissez **Joindre**. Créez un nouveau résolveur à l'aide du code suivant :

   ```
   import { util } from '@aws-appsync/utils'
   
   import { util } from '@aws-appsync/utils';
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     let condition = null;
     if (ctx.args.expectedVersion) {
       condition = {
         or: [
           { id: { attributeExists: false } },
           { version: { eq: ctx.args.expectedVersion } },
         ],
       };
     }
     return ddb.remove({ key: { id: ctx.args.id }, condition });
   }
   
   export function response(ctx) {
     const { error, result } = ctx;
     if (error) {
       util.appendError(error.message, error.type);
     }
     return result;
   }
   ```
**Note**  
L'`expectedVersion`argument est facultatif. Si l'appelant définit un `expectedVersion` argument dans la demande, le gestionnaire de demandes ajoute une condition qui permet à la `DeleteItem` demande de réussir uniquement si l'article est déjà supprimé ou si l'`version`attribut de la publication dans Amazon 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'élément `version` ou qu'il existe ou non dans Amazon DynamoDB.  
Même si vous supprimez un article, vous pouvez le renvoyer s'il ne l'a pas déjà été.

Pour plus d'informations sur la `DeleteItem` demande, consultez la [DeleteItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-deleteitem)documentation.

### Appelez l'API pour supprimer une publication
<a name="call-api-delete"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `delete` mutation entrante en une opération Amazon DynamoDB`DeleteItem`. Vous pouvez désormais exécuter une mutation pour supprimer quelque chose dans la table.

**Pour exécuter votre mutation**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante. Vous devrez également mettre à jour l'`id`argument avec la valeur que vous avez notée précédemment :

   ```
   mutation deletePost {
     deletePost(id:123) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`deletePost`.

1. La publication est supprimée d'Amazon DynamoDB. **Notez que cela AWS AppSync renvoie la valeur de l'élément qui a été supprimé d'Amazon DynamoDB, qui doit apparaître dans **le** volet Résultats à droite du volet 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
       }
     }
   }
   ```

1. La valeur n'est renvoyée que si cet appel à `deletePost` est celui qui la supprime réellement d'Amazon DynamoDB. Choisissez **à nouveau Exécuter**.

1. L'appel réussit toujours, mais aucune valeur n'est renvoyée :

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

1. Essayons maintenant de supprimer un message, mais cette fois en spécifiant un`expectedValue`. Tout d'abord, vous devez créer une nouvelle publication, car vous venez de supprimer celle sur laquelle vous avez travaillé jusqu'à présent.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante :

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

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`addPost`.

1. Les résultats de la publication nouvellement créée doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. Enregistrez le `id` nom de l'objet nouvellement créé car vous en aurez besoin dans un instant. 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
       }
     }
   }
   ```

1. Essayons maintenant de supprimer cette publication avec une valeur illégale pour **ExpectedVersion**. Dans le volet **Requêtes**, ajoutez la mutation suivante. Vous devrez également mettre à jour l'`id`argument avec 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
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`deletePost`. Le résultat suivant est renvoyé :

   ```
   {
     "data": {
       "deletePost": null
     },
     "errors": [
       {
         "path": [
           "deletePost"
         ],
         "data": null,
         "errorType": "DynamoDB:ConditionalCheckFailedException",
         "errorInfo": null,
         "locations": [
           {
             "line": 2,
             "column": 3,
             "sourceName": null
           }
         ],
         "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 7083O037M1FTFRK038A4CI9H43VV4KQNSO5AEMVJF66Q9ASUAAJG)"
       }
     ]
   }
   ```

1. La demande a échoué car l'expression de condition est évaluée à`false`. La valeur `version` de la publication dans Amazon 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
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`deletePost`. 

   Cette fois, la demande aboutit et la valeur supprimée d'Amazon 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
       }
     }
   }
   ```

1. Choisissez **à nouveau Exécuter**. L'appel réussit toujours, mais cette fois aucune valeur n'est renvoyée car la publication a déjà été supprimée dans Amazon DynamoDB.

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

## Configuration d'un résolveur AllPost (Amazon DynamoDB Scan)
<a name="configure-allpost"></a>

Jusqu'à présent, l'API n'est utile que si vous connaissez le `id` message que vous souhaitez consulter. Ajoutons un nouveau résolveur qui renvoie toutes les publications de la table.

**Pour ajouter votre mutation**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPost` :

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

1. Ajouter un nouveau type `PaginationPosts` :

   ```
   type PaginatedPosts {
       posts: [Post!]!
       nextToken: String
   }
   ```

1. Choisissez **Sauvegarder schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `allPost` champ nouvellement créé dans le `Query` type, puis choisissez **Joindre**. Créez un nouveau résolveur avec le code suivant :

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken } = ctx.arguments;
     return ddb.scan({ limit, nextToken });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

   Le gestionnaire de requêtes de ce résolveur attend deux arguments facultatifs : 
   + `limit`- Spécifie le nombre maximum d'éléments à renvoyer en un seul appel.
   + `nextToken`- Utilisé pour récupérer la prochaine série de résultats (nous montrerons d'où `nextToken` vient la valeur pour plus tard).

1. Enregistrez toutes les modifications apportées à votre résolveur.

Pour plus d'informations sur la `Scan` demande, consultez la documentation de référence du [scan](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-scan).

### Appelez l'API pour scanner tous les posts
<a name="call-api-scan"></a>

Maintenant que le résolveur est configuré, AWS AppSync il sait comment traduire une `allPost` requête entrante en une opération Amazon 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à.

**Pour ajouter et interroger des données**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante :

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

1. Choisissez **Exécuter** (le bouton de lecture orange). 

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

   ```
   query allPost {
     allPost(limit: 5) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPost`.

   Les cinq premiers articles doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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": "<token>"
       }
     }
   }
   ```

1. Vous avez reçu cinq résultats et un `nextToken` que vous pouvez utiliser pour obtenir la prochaine série de résultats. 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(
       limit: 5
       nextToken: "<token>"
     ) {
       posts {
         id
         author
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPost`.

   Les quatre publications restantes devraient apparaître dans le volet **Résultats** à droite du volet **Requêtes**. Il n'y en a pas `nextToken` dans cette série de résultats parce que vous avez parcouru les neuf articles et qu'il n'en reste aucun. 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 d'un résolveur d' allPostsByauteur (requête Amazon DynamoDB)
<a name="configure-query"></a>

Outre l'analyse de toutes les publications sur Amazon DynamoDB, vous pouvez également interroger Amazon DynamoDB pour récupérer les publications créées par un auteur spécifique. La table Amazon 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 Amazon DynamoDB pour récupérer toutes les `Query` publications créées par un auteur spécifique.

**Pour ajouter votre requête**

1. Dans votre API, choisissez l'onglet **Schéma**.

1. Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPostsByAuthor` :

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

   Notez que cela utilise le même `PaginatedPosts` type que celui que vous avez utilisé pour la `allPost` requête.

1. Choisissez **Sauvegarder schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `allPostsByAuthor` champ nouvellement créé sur le `Query` type, puis choisissez **Joindre**. Créez un résolveur à l'aide de l'extrait ci-dessous :

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken, author } = ctx.arguments;
     return ddb.query({
       index: 'author-index',
       query: { author: { eq: author } },
       limit,
       nextToken,
     });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

   Comme le `allPost` résolveur, ce résolveur possède deux arguments facultatifs :
   + `limit`- Spécifie le nombre maximum d'éléments à renvoyer en un seul appel.
   + `nextToken`- Récupère le prochain ensemble de résultats (la valeur de `nextToken` peut être obtenue à partir d'un appel précédent).

1. Enregistrez toutes les modifications apportées à votre résolveur.

Pour plus d'informations sur la `Query` demande, consultez la documentation de référence sur les [requêtes](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-query).

### Appelez l'API pour interroger tous les articles par auteur
<a name="call-api-query"></a>

Maintenant que le résolveur a été 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, cependant, remplissons le tableau avec quelques articles supplémentaires, car tous les articles ont jusqu'à présent le même auteur.

**Pour ajouter des données et effectuer une requête**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante :

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

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`addPost`.

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

   ```
   query allPostsByAuthor {
     allPostsByAuthor(author: "Nadia") {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPostsByAuthor`. Toutes les publications rédigées par `Nadia` doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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
       }
     }
   }
   ```

1. 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.

1. Dans le volet **Requêtes**, ajoutez la requête suivante : 

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "AUTHORNAME"
       limit: 5
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPostsByAuthor`. Toutes les publications rédigées par `AUTHORNAME` doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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": "<token>"
       }
     }
   }
   ```

1. Mettez à jour l'argument `nextToken` avec la valeur renvoyée par la requête précédente :

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "AUTHORNAME"
       limit: 5
       nextToken: "<token>"
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPostsByAuthor`. Les autres publications rédigées par `AUTHORNAME` doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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
       }
     }
   }
   ```

## Utiliser 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 votre résolveur, tels que des ensembles, des listes et des cartes. Mettons à jour notre type `Post` pour inclure des balises. Une publication peut comporter zéro ou plusieurs balises, 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.

**Pour configurer vos données**

1. Dans votre API, choisissez l'onglet **Schéma**. 

1. 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!]
   }
   ```

1. Dans le volet **Schéma**, modifiez le type `Query` pour ajouter une nouvelle requête `allPostsByTag` :

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

1. 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!
   }
   ```

1. Choisissez **Sauvegarder schéma**.

1. Dans le volet **Résolveurs** sur la droite, recherchez le `allPostsByTag` champ nouvellement créé sur le `Query` type, puis choisissez **Joindre**. Créez votre résolveur à l'aide de l'extrait ci-dessous :

   ```
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     const { limit = 20, nextToken, tag } = ctx.arguments;
     return ddb.scan({ limit, nextToken, filter: { tags: { contains: tag } } });
   }
   
   export function response(ctx) {
     const { items: posts = [], nextToken } = ctx.result;
     return { posts, nextToken };
   }
   ```

1. Enregistrez toutes les modifications que vous avez apportées à votre résolveur.

1. Maintenant, faites de même pour le `Mutation` champ `addTag` en utilisant l'extrait ci-dessous :
**Note**  
Bien que les utilitaires DynamoDB ne prennent actuellement pas en charge les opérations relatives aux ensembles, vous pouvez toujours interagir avec les ensembles en créant vous-même la requête.

   ```
   import { util } from '@aws-appsync/utils'
   
   export function request(ctx) {
   	const { id, tag } = ctx.arguments
   	const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 })
   	expressionValues[':tags'] = util.dynamodb.toStringSet([tag])
   
   	return {
   		operation: 'UpdateItem',
   		key: util.dynamodb.toMapValues({ id }),
   		update: {
   			expression: `ADD tags :tags, version :plusOne`,
   			expressionValues,
   		},
   	}
   }
   
   export const response = (ctx) => ctx.result
   ```

1. Enregistrez toutes les modifications apportées à votre résolveur.

1. Répétez cette opération une fois de plus pour le `Mutation` champ `removeTag` à l'aide de l'extrait ci-dessous :

   ```
   import { util } from '@aws-appsync/utils';
   	
   export function request(ctx) {
   	  const { id, tag } = ctx.arguments;
   	  const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 });
   	  expressionValues[':tags'] = util.dynamodb.toStringSet([tag]);
   	
   	  return {
   	    operation: 'UpdateItem',
   	    key: util.dynamodb.toMapValues({ id }),
   	    update: {
   	      expression: `DELETE tags :tags ADD version :plusOne`,
   	      expressionValues,
   	    },
   	  };
   	}
   	
   	export const response = (ctx) => ctx.resultexport
   ```

1. Enregistrez toutes les modifications apportées à votre résolveur.

### Appel de l'API pour utiliser des balises
<a name="call-api-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`.

**Pour utiliser des balises**

1. Dans votre API, choisissez l'onglet **Requêtes**.

1. Dans le volet **Requêtes**, ajoutez la requête suivante :

   ```
   query allPostsByAuthor {
     allPostsByAuthor(
       author: "Nadia"
     ) {
       posts {
         id
         title
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPostsByAuthor`.

1. Toutes les publications de Nadia doivent apparaître dans le volet **Résultats** à droite du volet **Requêtes**. 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
       }
     }
   }
   ```

1. Utilisons celui qui s'intitule Le *chien le plus mignon du monde.* Enregistrez-le `id` car vous l'utiliserez plus tard. Essayons maintenant d'ajouter un `dog` tag.

1. Dans le volet **Requêtes**, ajoutez la mutation suivante. 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
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`addTag`. Le message est mis à jour avec le nouveau tag :

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

1. Vous pouvez ajouter d'autres tags. Mettez à jour la mutation pour remplacer l'`tag`argument par `puppy` :

   ```
   mutation addTag {
     addTag(id:10 tag: "puppy") {
       id
       title
       tags
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`addTag`. Le message est mis à jour avec le nouveau tag :

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

1. Vous pouvez également supprimer des balises. Dans le volet **Requêtes**, ajoutez la mutation suivante. Vous devrez également mettre à jour l'`id`argument avec la valeur que vous avez notée précédemment :

   ```
   mutation removeTag {
     removeTag(id:10 tag: "puppy") {
       id
       title
       tags
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`removeTag`. 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"
         ]
       }
     }
   }
   ```

1. Vous pouvez également rechercher tous les articles comportant un tag. Dans le volet **Requêtes**, ajoutez la requête suivante : 

   ```
   query allPostsByTag {
     allPostsByTag(tag: "dog") {
       posts {
         id
         title
         tags
       }
       nextToken
     }
   }
   ```

1. Choisissez **Exécuter** (le bouton de lecture orange), puis choisissez`allPostsByTag`. 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
       }
     }
   }
   ```

## Conclusion
<a name="conclusion-dynamodb-tutorial-js"></a>

Dans ce didacticiel, vous avez créé une API qui vous permet de manipuler des `Post` objets dans DynamoDB à l'aide de AWS AppSync GraphQL. 

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

**Pour supprimer le rôle associé à votre table DynamoDB, sélectionnez votre source de données dans **le tableau Sources de données et cliquez sur** Modifier.** Notez la valeur du rôle sous **Créer ou utiliser un rôle existant**. Accédez à la console IAM pour supprimer le rôle.

Pour supprimer votre table DynamoDB, cliquez sur le nom de la table dans la liste des sources de données. Cela vous amène à la console DynamoDB où vous pouvez supprimer la table. 

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

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.

## Outils électriques pour AWS Lambda
<a name="powertools-graphql"></a>

Le gestionnaire d'événements Powertools for AWS Lambda GraphQL simplifie le routage et le traitement des événements GraphQL dans les fonctions Lambda. Il est disponible pour Python et Typescript. Pour en savoir plus sur le gestionnaire d'événements de l'API GraphQL, consultez les références suivantes dans la AWS Lambda documentation Powertools.
+ [Gestionnaire d'événements Powertools pour AWS Lambda GraphQL (Python)](https://docs.aws.amazon.com/powertools/python/latest/core/event_handler/appsync/)
+ [Gestionnaire d'événements Powertools pour AWS Lambda GraphQL (Typescript)](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-graphql/) 

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

L'exemple suivant montre une fonction Lambda écrite en `Node.js` (runtime : Node.js 18.x) qui effectue différentes opérations sur des articles de blog dans le cadre d'une application de publication de blog. Notez que le code doit être enregistré dans un nom de fichier portant l'extension .mis.

```
export const handler = async (event) => {
console.log('Received event {}', JSON.stringify(event, 3))

  const 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', },
  }

  const 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.')
  let result
  switch (event.field) {
case 'getPost':
      return posts[event.arguments.id]
    case 'allPosts':
      return Object.values(posts)
    case 'addPost':
      // return the arguments back
return event.arguments
    case 'addPostErrorWithData':
      result = posts[event.arguments.id]
      // attached additional error information to the post
      result.errorMessage = 'Error with the mutation, data has changed'
      result.errorType = 'MUTATION_ERROR'
return result
    case 'relatedPosts':
      return relatedPosts[event.source.id]
    default:
      throw new Error('Unknown field, unable to resolve ' + event.field)
  }
}
```

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. 

**Note**  
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.

## Configuration d'une source de données pour Lambda
<a name="configure-data-source-for-lamlong-js"></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. 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-js"></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-js"></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.

Vous allez créer un résolveur qui utilise le runtime AWS AppSync JavaScript (`APPSYNC_JS`) et qui interagit avec vos fonctions Lambda. Pour en savoir plus sur l'écriture de AWS AppSync résolveurs et de fonctions avec JavaScript, consultez les [fonctionnalités JavaScript d'exécution pour les résolveurs et les](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) fonctions.

Pour plus d'informations sur les modèles de mappage Lambda, consultez la référence des [fonctions de JavaScript résolution pour Lambda](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-lambda-js.html).

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, dans le volet **Résolveurs**, choisissez **Attacher** à côté du `getPost(id:ID!): Post` champ. Choisissez votre source de données Lambda. Entrez ensuite le code suivant :

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

export function response(ctx) {
  return ctx.result;
}
```

Ce code de résolution transmet le nom du champ, la liste des arguments et le contexte de l'objet source à la fonction Lambda lorsqu'elle l'invoque. Choisissez **Enregistrer**.

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

## Testez votre API GraphQL
<a name="testing-your-graphql-api-js"></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-js"></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-js"></a>

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

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

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

## Erreurs de renvoi
<a name="returning-errors-js"></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 :
+ Gestionnaire de réponses Resolver
+ Fonction Lambda

### À partir du gestionnaire de réponses du résolveur
<a name="from-the-resolver-response-handler-js"></a>

Pour signaler des erreurs intentionnelles, vous pouvez utiliser la méthode `util.error` utilitaire. Il prend un argument an `errorMessage``errorType`, un et une `data` valeur optionnelle. 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 l'utiliser dans le gestionnaire de réponses du `Post.relatedPosts: [Post]` résolveur.

```
// the Post.relatedPosts response handler
export function response(ctx) {
    util.error("Failed to fetch relatedPosts", "LambdaFailure", ctx.result)
    return ctx.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-js"></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. 

**Note**  
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 :

```
export const handler = async (event) => {
  console.log('Received event {}', JSON.stringify(event, 3))
  throw new Error('I always fail.')
}
```

L'erreur est reçue dans votre gestionnaire de réponses. Vous pouvez le renvoyer dans la réponse GraphQL en ajoutant l'erreur à la réponse avec. `util.appendError` Pour ce faire, modifiez le gestionnaire de réponse de votre AWS AppSync fonction comme suit :

```
// the lambdaInvoke response handler
export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    util.appendError(error.message, error.type, result);
  }
  return result;
}
```

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

```
{
  "data": {
    "allPosts": null
  },
  "errors": [
    {
      "path": [
        "allPosts"
      ],
      "data": null,
      "errorType": "Lambda:Unhandled",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "I fail. always"
    }
  ]
}
```

## Cas d'utilisation avancé : traitement par lots
<a name="advanced-use-case-batching-js"></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 {   // 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 {   // 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 le démontrer, mettons à jour le résolveur pour `relatedPosts` qu'il gère le traitement par lots.

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: ctx.info.fieldName === 'relatedPosts' ? 'BatchInvoke' : 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    util.appendError(error.message, error.type, result);
  }
  return result;
}
```

Le code change désormais l'opération de `Invoke` à `BatchInvoke` celle en `fieldName` cours de résolution`relatedPosts`. Activez maintenant le traitement par lots sur la fonction dans la section **Configurer le traitement par lots.** Définissez la taille de lot maximale définie sur. `5` Choisissez **Enregistrer**.

Avec cette modification, lors de la résolution`relatedPosts`, 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 la demande, 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
]
```

Vous pouvez mettre à jour votre code Lambda pour gérer le traitement par lots pour : `relatedPosts`

```
export const handler = async (event) => {
  console.log('Received event {}', JSON.stringify(event, 3))
  //throw new Error('I fail. always')

  const 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', },
  }

  const relatedPosts = {
    1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }
  
  if (!event.field && event.length){
    console.log(`Got a BatchInvoke Request. The payload has ${event.length} items to resolve.`);
    return event.map(e => relatedPosts[e.source.id])
  }

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

### Renvoi d'erreurs individuelles
<a name="returning-individual-errors-js"></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 de votre gestionnaire de réponses. 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 :

```
export const handler = async (event) => {
console.log('Received event {}', JSON.stringify(event, 3))
  // throw new Error('I fail. always')
const 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', },
  }

  const relatedPosts = {
1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }
  
  if (!event.field && event.length){
console.log(`Got a BatchInvoke Request. The payload has ${event.length} items to resolve.`);
    return event.map(e => {
// return an error for post 2
if (e.source.id === '2') {
return { 'data': null, 'errorMessage': 'Error Happened', 'errorType': 'ERROR' }
      }
      return {data: relatedPosts[e.source.id]}
      })
  }

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

Mettez à jour le code du `relatedPosts` résolveur :

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const {source, args} = ctx
  return {
    operation: ctx.info.fieldName === 'relatedPosts' ? 'BatchInvoke' : 'Invoke',
    payload: { field: ctx.info.fieldName, arguments: args, source },
  };
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    util.appendError(error.message, error.type, result);
  } else if (result.errorMessage) {
    util.appendError(result.errorMessage, result.errorType, result.data)
  } else if (ctx.info.fieldName === 'relatedPosts') {
      return result.data
  } else {
      return result
  }
}
```

Le gestionnaire de réponses vérifie désormais les erreurs renvoyées par la `Invoke` fonction Lambda lors des opérations, vérifie les erreurs renvoyées pour les éléments individuels des `BatchInvoke` opérations et vérifie enfin le. `fieldName` Car`relatedPosts`, la fonction revient`result.data`. Pour tous les autres champs, la fonction renvoie simplement`result`. Par exemple, consultez la requête ci-dessous :

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

Cette requête renvoie une réponse GraphQL similaire à la suivante :

```
{
  "data": {
    "allPosts": [
      {
        "id": "1",
        "relatedPosts": [
          {
            "id": "4"
          }
        ]
      },
      {
        "id": "2",
        "relatedPosts": null
      },
      {
        "id": "3",
        "relatedPosts": [
          {
            "id": "2"
          },
          {
            "id": "1"
          }
        ]
      },
      {
        "id": "4",
        "relatedPosts": [
          {
            "id": "2"
          },
          {
            "id": "1"
          }
        ]
      },
      {
        "id": "5",
        "relatedPosts": []
      }
    ]
  },
  "errors": [
    {
      "path": [
        "allPosts",
        1,
        "relatedPosts"
      ],
      "data": null,
      "errorType": "ERROR",
      "errorInfo": null,
      "locations": [
        {
          "line": 4,
          "column": 5,
          "sourceName": null
        }
      ],
      "message": "Error Happened"
    }
  ]
}
```

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

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 \
 --code "<code-goes-here>" \
 --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
 --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.

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

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 **transmettra** simplement le résultat du gestionnaire de demandes au gestionnaire de réponses. La résolution du champ ne quitte pas AWS AppSync.

Les résolveurs locaux sont utiles dans de nombreuses situations. 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, créons une application pub/sub dans laquelle les utilisateurs peuvent publier des messages et s'y abonner. 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 pub/sub
<a name="create-the-pub-sub-application-js"></a>

Créez d'abord une API GraphQL vierge en choisissant l'option **Design from scratch** et en configurant les détails facultatifs lors de la création de votre API GraphQL.

Dans notre application pub/sub, les clients peuvent s'abonner à des messages et les publier. Chaque message publié inclut un nom et des données. Ajoutez ceci au schéma :

```
type Channel {
	name: String!
	data: AWSJSON!
}

type Mutation {
	publish(name: String!, data: AWSJSON!): Channel
}

type Query {
	getChannel: Channel
}

type Subscription {
	subscribe(name: String!): Channel
		@aws_subscribe(mutations: ["publish"])
}
```

Attachons ensuite un résolveur au `Mutation.publish` champ. Dans le volet **Résolveurs** situé à côté du volet **Schéma**, recherchez le `Mutation` type, puis le `publish(...): Channel` champ, puis cliquez sur **Joindre**.

Créez une source de données *None* et nommez-la *PageDataSource*. Attachez-le à votre résolveur.

Ajoutez votre implémentation de résolveur à l'aide de l'extrait suivant :

```
export function request(ctx) {
  return { payload: ctx.args };
}

export function response(ctx) {
  return ctx.result;
}
```

Assurez-vous de créer le résolveur et d'enregistrer les modifications que vous avez apportées.

## Envoyer des messages et s'y abonner
<a name="send-and-subscribe-to-messages-js"></a>

Pour que les clients puissent recevoir des messages, ils doivent d'abord être abonnés à une boîte de réception.

Dans le volet **Requêtes**, exécutez l'`SubscribeToData`abonnement :

```
subscription SubscribeToData {
    subscribe(name:"channel") {
        name
        data
    }
}
```

 L'abonné recevra des messages chaque fois que la `publish` mutation est invoquée, mais uniquement lorsque le message est envoyé à l'`channel`abonnement. Essayons cela dans le volet **Requêtes**. Pendant que votre abonnement est toujours en cours d'exécution dans la console, ouvrez une autre console et exécutez la requête suivante dans le volet **Requêtes** :

**Note**  
Nous utilisons des chaînes JSON valides dans cet exemple.

```
mutation PublishData {
    publish(data: "{\"msg\": \"hello world!\"}", name: "channel") {
        data
        name
    }
}
```

Le résultat doit se présenter comme suit :

```
{
  "data": {
    "publish": {
      "data": "{\"msg\":\"hello world!\"}",
      "name": "channel"
    }
  }
}
```

Nous venons de démontrer l'utilisation de résolveurs locaux, en publiant un message et en le recevant sans quitter le AWS AppSync service.

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

Les résolveurs et les champs d'un schéma GraphQL possèdent des relations 1:1 avec un haut niveau de flexibilité. Comme une source de données est configurée sur un résolveur indépendamment d'un schéma, vous avez la possibilité de résoudre ou de manipuler vos types de GraphQL via différentes sources de données, ce qui vous permet de combiner un schéma pour répondre au mieux à vos besoins.

Les scénarios suivants montrent comment mélanger et associer des sources de données dans votre schéma. Avant de commencer, vous devez être familiarisé avec la configuration des sources de données et des résolveurs pour AWS Lambda Amazon DynamoDB et Amazon Service. OpenSearch 

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

Le schéma suivant a un type `Post` de trois `Query` `Mutation` opérations chacune :

```
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 auriez un total de six résolveurs, chacun ayant besoin d'une source de données. Une façon de résoudre ce problème serait de les connecter à une seule table Amazon DynamoDB, `Posts` appelée, dans laquelle le champ exécute une analyse et `AllPost` le champ exécute une requête ([JavaScriptvoir la référence de `searchPosts` la fonction de résolution](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html) pour DynamoDB). Cependant, vous n'êtes pas limité à Amazon DynamoDB ; différentes sources de données, telles que Lambda OpenSearch ou Service, existent pour répondre aux besoins de votre entreprise. 

## Modification des données par le biais de résolveurs
<a name="alter-data-through-resolvers-js"></a>

Vous devrez peut-être renvoyer des résultats provenant d'une base de données tierce qui n'est pas directement prise en charge par AWS AppSync les sources de données. Il se peut également que vous deviez effectuer des modifications complexes sur les données avant qu'elles ne soient renvoyées au (x) client (s) de l'API. Cela peut être dû à un formatage incorrect des types de données, tel que des différences d'horodatage sur les clients ou à la gestion de problèmes de rétrocompatibilité. Dans ce cas, connecter AWS Lambda des fonctions en tant que source de données à votre AWS AppSync API est la solution appropriée. À des fins d'illustration, dans l'exemple suivant, une AWS Lambda fonction manipule les données extraites d'un magasin de données tiers :

```
export const handler = (event, context, callback) => {
    // fetch data
    const result = fetcher()

    // apply complex business logic
    const data = transform(result)	

    // return to AppSync
    return data
};
```

Il s'agit d'une fonction Lambda parfaitement valide qui peut être attachée à un champ `AllPost` 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-js"></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 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 puissant lorsque vous ajoutez des opérations de recherche avancées à vos applications, telles que des mots clés, des correspondances de mots flous ou même des recherches géospatiales. Le transfert de données depuis DynamoDB peut être effectué par le biais d'un processus ETL, ou vous pouvez également diffuser des données depuis DynamoDB à l'aide de Lambda.

Pour commencer à utiliser ces sources de données spécifiques, consultez nos didacticiels [DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-resolvers-js.html) [et](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers-js.html) Lambda.

Par exemple, en utilisant le schéma de notre précédent didacticiel, la mutation suivante ajoute un élément à DynamoDB :

```
mutation addPost {
  addPost(
    id: 123
    author: "Nadia"
    title: "Our first post!"
    content: "This is our first post."
    url: "https://aws.amazon.com/appsync/"
  ) {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}
```

Cela écrit des données dans DynamoDB, qui les diffuse ensuite via Lambda vers OpenSearch Amazon Service, que vous utilisez ensuite pour rechercher des publications 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
    }
}

---------- or ----------

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 `allPost{...}` les requêtes et. `getPost{...}` Cette pile utilise l'exemple de code suivant pour les flux DynamoDB :

**Note**  
Ce code Python est un exemple et n'est pas destiné à être utilisé dans le code de production.

```
import boto3
import requests
from requests_aws4auth import AWS4Auth

region = '' # e.g. us-east-1
service = 'es'
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)

host = '' # the OpenSearch Service domain, e.g. https://search-mydomain.us-west-1.es.amazonaws.com
index = 'lambda-index'
datatype = '_doc'
url = host + '/' + index + '/' + datatype + '/'

headers = { "Content-Type": "application/json" }

def handler(event, context):
    count = 0
    for record in event['Records']:
        # Get the primary key for use as the OpenSearch ID
        id = record['dynamodb']['Keys']['id']['S']

        if record['eventName'] == 'REMOVE':
            r = requests.delete(url + id, auth=awsauth)
        else:
            document = record['dynamodb']['NewImage']
            r = requests.put(url + id, auth=awsauth, json=document, headers=headers)
        count += 1
    return str(count) + ' records processed.'
```

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 résolveurs Amazon OpenSearch Service dans AWS AppSync
<a name="tutorial-elasticsearch-resolvers-js"></a>

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 notre [référence des JavaScript fonctions de résolution pour OpenSearch](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-elasticsearch-js.html).

## Création d'un nouveau domaine OpenSearch de service
<a name="create-a-new-es-domain-js"></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 la AWS CloudFormation pile suivante dans la région US-West-2 (Oregon) sur votre compte : AWS 

 [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 d'une source de données pour le OpenSearch service
<a name="configure-data-source-for-es-js"></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 **Créer une source de données** 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 de OpenSearch service répertorié. Après l'avoir sélectionné, vous pouvez soit créer un nouveau rôle et attribuer AWS AppSync les autorisations appropriées au rôle, soit choisir un rôle existant, dont la politique intégrée est la suivante :

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 devez ajouter une politique similaire à celle ci-dessous 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 AWS AppSync données, qui se trouve dans la console IAM si vous laissez cette console le créer.

## Connecter un résolveur
<a name="connecting-a-resolver-js"></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 :

```
 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 à la racine du chemin `/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. Dans le volet **Résolveurs**, recherchez `searchPosts` et choisissez **Attacher**. Choisissez votre source OpenSearch de données de service et enregistrez le résolveur. Mettez à jour le code de votre résolveur à l'aide de l'extrait ci-dessous :

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by using an input term
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: `/post/_search`,
		params: { body: { from: 0, size: 50 } },
	}
}

/**
 * Returns the fetched items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result.hits.hits.map((hit) => hit._source)
}
```

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 les mettre à jour en conséquence.

## Modifier vos recherches
<a name="modifying-your-searches-js"></a>

Le gestionnaire de demandes de résolution 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 souhaitiez que cet auteur soit un argument défini dans votre requête GraphQL. Dans l'éditeur de **schéma** de la AWS AppSync console, ajoutez une `allPostsByAuthor` requête :

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

Dans le volet **Résolveurs**, recherchez `allPostsByAuthor` et choisissez **Attacher**. Choisissez la source OpenSearch de données du service et utilisez le code suivant :

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: '/post/_search',
		params: {
			body: {
				from: 0,
				size: 50,
				query: { match: { author: ctx.args.author } },
			},
		},
	}
}

/**
 * Returns the fetched items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result.hits.hits.map((hit) => hit._source)
}
```

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 éventuellement utiliser des informations préremplies, telles que du texte standard.

## Ajouter des données au OpenSearch service
<a name="adding-data-to-es-js"></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 peut servir de mécanisme pour 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** pour la `addPost()` mutation. Sélectionnez à nouveau la source de données du OpenSearch service et utilisez le code suivant :

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'PUT',
		path: `/post/_doc/${ctx.args.id}`,
		params: { body: ctx.args },
	}
}

/**
 * Returns the inserted post
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result
}
```

Comme précédemment, il s'agit d'un exemple de la manière dont vos données peuvent être structurées. Si vous avez des noms de champs ou des index différents, vous devez mettre à jour le `path` et`body`. Cet exemple montre également comment utiliser`context.arguments`, ce qui peut également être écrit sous forme de`ctx.args`, dans votre gestionnaire de demandes.

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

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

```
import { util } from '@aws-appsync/utils'

/**
 * Searches for documents by `author`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the request
 */
export function request(ctx) {
	return {
		operation: 'GET',
		path: `/post/_doc/${ctx.args.id}`,
	}
}

/**
 * Returns the post
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the result
 */
export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type)
	}
	return ctx.result._source
}
```

## Effectuer des requêtes et des mutations
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations-js"></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 search {
    searchPosts {
        id
        title
        author
        content
    }
}
```

## Bonnes pratiques
<a name="best-practices-js"></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](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-combining-graphql-resolvers-js.html) 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.

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

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 en une seule requête et renvoi des résultats à partir d'une table
+ Lecture d'enregistrements d'une ou de plusieurs tables en une seule requête
+ Écrire des enregistrements de transactions sur une ou plusieurs tables d'une all-or-nothing manière ou d'une autre
+ Exécution de transactions lorsque certaines conditions sont satisfaites

## Permissions
<a name="permissions-js"></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/*"
            ]
        }
    ]
}
```

------

**Note**  
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. Les sources de données configurées pour effectuer des extractions par rapport à DynamoDB n'ont qu'une seule table spécifiée pour simplifier les configurations. 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 transaction par rapport aux tables est effectuée dans le code du résolveur, que nous décrivons ci-dessous.

## Source de données
<a name="data-source-js"></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. 

**Nous aurons deux tables appelées **SavingAccounts** **et** CheckingAccounts, toutes deux `accountNumber` avec comme clé de partition, et une table TransactionHistory avec comme clé de partition.** `transactionId` Vous pouvez utiliser les commandes CLI ci-dessous pour créer vos tables. Assurez-vous de le remplacer `region` par votre région.

**Avec la CLI**

```
aws dynamodb create-table --table-name savingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name checkingAccounts \
  --attribute-definitions AttributeName=accountNumber,AttributeType=S \
  --key-schema AttributeName=accountNumber,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region

aws dynamodb create-table --table-name transactionHistory \
  --attribute-definitions AttributeName=transactionId,AttributeType=S \
  --key-schema AttributeName=transactionId,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD --region region
```

Dans la AWS AppSync console, dans **Sources de données**, créez une nouvelle source de données DynamoDB et nommez-la. **TransactTutorial** Sélectionnez **SavingAccounts** comme tableau (même si le tableau spécifique n'a pas d'importance lorsque vous utilisez des transactions). Choisissez de créer un nouveau rôle et une nouvelle source de données. Vous pouvez consulter la configuration de la source de données pour connaître le nom du rôle généré. Dans la console IAM, vous pouvez ajouter une politique en ligne qui permet à la source de données d'interagir avec toutes les tables.

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-js"></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
}
```

### TransactWriteItems - Renseignez les comptes
<a name="transactwriteitems-populate-accounts-js"></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. Choisissez la source `TransactTutorial` de données, puis sélectionnez **Créer**.

Utilisez maintenant le code suivant :

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccounts, checkingAccounts } = ctx.args

	const savings = savingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'savingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})

	const checkings = checkingAccounts.map(({ accountNumber, ...rest }) => {
		return {
			table: 'checkingAccounts',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ accountNumber }),
			attributeValues: util.dynamodb.toMapValues(rest),
		}
	})
	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const { savingAccounts: sInput, checkingAccounts: cInput } = ctx.args
	const keys = ctx.result.keys
	const savingAccounts = sInput.map((_, i) => keys[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => keys[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

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 trois comptes d'épargne et trois comptes chèques 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-js"></a>

Attachez un résolveur à la `transferMoney` mutation avec le code suivant. Pour chaque transfert, nous avons besoin d'un modificateur de réussite à la fois pour le compte courant et le compte d'épargne, et nous devons suivre le transfert en termes de transactions.

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const transactions = ctx.args.transactions

	const savings = []
	const checkings = []
	const history = []
	transactions.forEach((t) => {
		const { savingAccountNumber, checkingAccountNumber, amount } = t
		savings.push({
			table: 'savingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: savingAccountNumber }),
			update: {
				expression: 'SET balance = balance - :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		checkings.push({
			table: 'checkingAccounts',
			operation: 'UpdateItem',
			key: util.dynamodb.toMapValues({ accountNumber: checkingAccountNumber }),
			update: {
				expression: 'SET balance = balance + :amount',
				expressionValues: util.dynamodb.toMapValues({ ':amount': amount }),
			},
		})
		history.push({
			table: 'transactionHistory',
			operation: 'PutItem',
			key: util.dynamodb.toMapValues({ transactionId: util.autoId() }),
			attributeValues: util.dynamodb.toMapValues({
				from: savingAccountNumber,
				to: checkingAccountNumber,
				amount,
			}),
		})
	})

	return {
		version: '2018-05-29',
		operation: 'TransactWriteItems',
		transactItems: [...savings, ...checkings, ...history],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}
	const tInput = ctx.args.transactions
	const tLength = tInput.length
	const keys = ctx.result.keys
	const savingAccounts = tInput.map((_, i) => keys[tLength * 0 + i])
	const checkingAccounts = tInput.map((_, i) => keys[tLength * 1 + i])
	const transactionHistory = tInput.map((_, i) => keys[tLength * 2 + i])
	return { savingAccounts, checkingAccounts, transactionHistory }
}
```

Accédez maintenant à la section **Requêtes** de la AWS AppSync console 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é trois 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érez des comptes
<a name="transactgetitems-retrieve-accounts-js"></a>

Afin de récupérer les informations relatives aux comptes d'épargne et aux comptes chèques en une seule demande transactionnelle, nous allons associer un résolveur à l'opération `Query.getAccounts` GraphQL sur notre schéma. Sélectionnez **Joindre**, puis choisissez la même source de `TransactTutorial` données créée au début du didacticiel. Utilisez le code suivant : 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { savingAccountNumbers, checkingAccountNumbers } = ctx.args

	const savings = savingAccountNumbers.map((accountNumber) => {
		return { table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	const checkings = checkingAccountNumbers.map((accountNumber) => {
		return { table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) }
	})
	return {
		version: '2018-05-29',
		operation: 'TransactGetItems',
		transactItems: [...savings, ...checkings],
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons)
	}

	const { savingAccountNumbers: sInput, checkingAccountNumbers: cInput } = ctx.args
	const items = ctx.result.items
	const savingAccounts = sInput.map((_, i) => items[i])
	const sLength = sInput.length
	const checkingAccounts = cInput.map((_, i) => items[sLength + i])
	return { savingAccounts, checkingAccounts }
}
```

Enregistrez le résolveur et accédez aux sections **Requêtes** de la console AWS AppSync . Pour récupérer les comptes d'épargne et les comptes chèques, 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 à l'aide de. AWS AppSync

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

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`. En utilisant ces fonctionnalités dans AWS AppSync, vous pouvez effectuer des tâches telles que :
+ Transmission d'une liste de clés en une seule requête et renvoi des résultats à partir d'une table
+ Lecture d'enregistrements d'une ou de plusieurs tables en une seule requête
+ Écrire des enregistrements en bloc dans une ou plusieurs tables
+ Écrire ou supprimer de manière conditionnelle des enregistrements dans plusieurs tables susceptibles d'avoir une relation

Les opérations par lots AWS AppSync présentent deux différences principales par rapport aux opérations non groupées :
+ Le rôle de source de données doit disposer d'autorisations sur toutes les tables auxquelles le résolveur aura accès.
+ La spécification de table pour un résolveur fait partie de l'objet de demande.

## Lots à table unique
<a name="single-table-batch-js"></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 commencer, créons une nouvelle API GraphQL. Dans la AWS AppSync console, choisissez **Create API** APIs, **GraphQL** et **Design from scratch**. **Nommez votre API`BatchTutorial API`, choisissez **Next**, puis à l'étape **Spécifier les ressources GraphQL**, choisissez Create **GraphQL resources later, puis** cliquez sur Next.** Vérifiez vos informations et créez l'API. Accédez à la page **Schéma** et collez le schéma suivant, en notant que pour la requête, nous allons transmettre 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]
}
```

Enregistrez votre schéma et choisissez **Create Resources** en haut de la page. Choisissez **Utiliser le type existant**, puis sélectionnez le `Post` type. Donnez un nom à votre table`Posts`. **Assurez-vous que la **clé primaire** est définie sur`id`, désélectionnez **Générer automatiquement GraphQL** (vous fournirez votre propre code), puis sélectionnez Créer.** Pour commencer, AWS AppSync crée une nouvelle table DynamoDB et une source de données connectée à la table avec les rôles appropriés. Cependant, vous devez encore ajouter quelques autorisations au rôle. Accédez à la page **Sources de données** et choisissez la nouvelle source de données. Sous **Sélectionnez un rôle existant**, vous remarquerez qu'un rôle a été automatiquement créé pour le tableau. Prenez note du rôle (qui devrait ressembler à quelque chose comme ça`appsync-ds-ddb-aaabbbcccddd-Posts`), puis accédez à la console IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). Dans la console IAM, choisissez **Rôles**, puis choisissez votre rôle dans le tableau. Dans votre rôle, sous **Politiques d'autorisations**, cliquez sur le bouton `+` « » à côté de la politique (le nom doit être similaire au nom du rôle). Choisissez **Modifier** en haut du pliable lorsque la politique apparaît. Vous devez ajouter des autorisations par lots à votre politique, en particulier `dynamodb:BatchGetItem` et`dynamodb:BatchWriteItem`. Cela ressemblera à ceci :

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

****  

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

------

Choisissez **Suivant**, puis **Enregistrer les modifications**. Votre politique devrait autoriser le traitement par lots dès maintenant.

De retour dans la AWS AppSync console, accédez à la page **Schéma** et sélectionnez **Joindre** à côté du `Mutation.batchAdd` champ. Créez votre résolveur en utilisant le `Posts` tableau comme source de données. Dans l'éditeur de code, remplacez les gestionnaires par l'extrait ci-dessous. Cet extrait prend automatiquement chaque élément du `input PostInput` type GraphQL et crée une carte, nécessaire à l'opération : `BatchPutItem`

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchPutItem",
    tables: {
      Posts: ctx.args.posts.map((post) => util.dynamodb.toMapValues(post)),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Accédez à la page **Requêtes** de la AWS AppSync console et exécutez la `batchAdd` mutation 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 ; cela peut être validé en consultant la console DynamoDB pour rechercher les valeurs écrites dans la table. `Posts`

Ensuite, répétez le processus consistant à joindre un résolveur, sauf pour le `Query.batchGet` champ utilisant la `Posts` table comme source de données. Remplacez les gestionnaires par le code ci-dessous. 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` :

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchGetItem",
    tables: {
      Posts: {
        keys: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
        consistentRead: true,
      },
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Retournez maintenant à la page **Requêtes** de la AWS AppSync console et exécutez la `batchGet` requête 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 qu'une `null` valeur a été renvoyée pour le `id` avec une valeur de`3`. Cela est dû au fait qu'il n'y avait pas encore d'enregistrement dans votre `Posts` table contenant 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 constitue une fonctionnalité supplémentaire qui AWS AppSync fonctionne en votre nom. Donc, si vous passez à`batchGet(ids:[1,3,2])`, vous verrez que la commande a changé. Vous saurez également quel `id` a renvoyé une valeur `null`.

Enfin, attachez un autre résolveur au `Mutation.batchDelete` champ en utilisant la `Posts` table comme source de données. Remplacez les gestionnaires par le code ci-dessous. 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` :

```
import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "BatchDeleteItem",
    tables: {
      Posts: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

Retournez maintenant à la page **Requêtes** de la AWS AppSync console et exécutez la `batchDelete` mutation 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`.

## Lot multi-tables
<a name="multi-table-batch-js"></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 sommes en train de créer une application de santé pour animaux de compagnie dans laquelle des capteurs signalent l'emplacement et la température corporelle de l'animal. 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.

Dans la AWS AppSync console, choisissez **Create API** APIs, **GraphQL** et **Design from scratch**. **Nommez votre API`MultiBatchTutorial API`, choisissez **Next**, puis à l'étape **Spécifier les ressources GraphQL**, choisissez Create **GraphQL resources later, puis** cliquez sur Next.** Vérifiez vos informations et créez l'API. Accédez à la page **Schéma**, collez et enregistrez le schéma 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
}
```

Nous devons créer deux tables DynamoDB :
+ `locationReadings`enregistrera les relevés de position du capteur.
+ `temperatureReadings`enregistrera les relevés de température du capteur.

Les deux tables partageront la même structure de clé primaire : `sensorId (String)` en tant que clé de partition et `timestamp (String)` en tant que clé de tri.

Choisissez **Créer des ressources** en haut de la page. Choisissez **Utiliser le type existant**, puis sélectionnez le `locationReadings` type. Donnez un nom à votre table`locationReadings`. Assurez-vous que la **clé primaire** est définie sur `sensorId` et que la clé de tri est définie sur`timestamp`. **Désélectionnez **Générer automatiquement GraphQL** (vous fournirez votre propre code), puis sélectionnez Créer.** Répétez ce processus pour `temperatureReadings` utiliser le `temperatureReadings` comme type et nom de table. Utilisez les mêmes touches que ci-dessus.

Vos nouvelles tables contiendront les rôles générés automatiquement. Vous devez encore ajouter quelques autorisations à ces rôles. Accédez à la page **Sources de données** et choisissez`locationReadings`. Sous **Sélectionner un rôle existant**, vous pouvez voir le rôle. Prenez note du rôle (qui devrait ressembler à quelque chose comme ça`appsync-ds-ddb-aaabbbcccddd-locationReadings`), puis accédez à la console IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). Dans la console IAM, choisissez **Rôles**, puis choisissez votre rôle dans le tableau. Dans votre rôle, sous **Politiques d'autorisations**, cliquez sur le bouton `+` « » à côté de la politique (le nom doit être similaire au nom du rôle). Choisissez **Modifier** en haut du pliable lorsque la politique apparaît. Vous devez ajouter des autorisations à cette politique. Cela ressemblera à ceci :

Choisissez **Suivant**, puis **Enregistrer les modifications**. Répétez ce processus pour la source de `temperatureReadings` données en utilisant le même extrait de politique ci-dessus.

### BatchPutItem - Enregistrement des lectures des capteurs
<a name="batchputitem-recording-sensor-readings-js"></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 devrons ajouter un résolveur à ce champ.

Sur la page **Schéma** de la AWS AppSync console, sélectionnez **Joindre** à côté du `Mutation.recordReadings` champ. Sur l'écran suivant, créez votre résolveur en utilisant le `locationReadings` tableau comme source de données.

Après avoir créé votre résolveur, remplacez les gestionnaires par le code suivant dans l'éditeur. Cette `BatchPutItem` opération nous permet de spécifier plusieurs tables : 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const locationReadings = locReadings.map((loc) => util.dynamodb.toMapValues(loc))
	const temperatureReadings = tempReadings.map((tmp) => util.dynamodb.toMapValues(tmp))

	return {
		operation: 'BatchPutItem',
		tables: {
			locationReadings,
			temperatureReadings,
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return 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.

**Note**  
L'utilisation de `utils.appendError()` est similaire à la`util.error()`, avec la principale différence qu'elle n'interrompt pas l'évaluation du gestionnaire de demandes ou de réponses. Il signale plutôt qu'une erreur s'est produite dans le champ, mais permet d'évaluer le gestionnaire et, par conséquent, de renvoyer les données à l'appelant. Nous vous recommandons de l'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 pouvons maintenant envoyer des relevés de 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é dix relevés de capteurs pour une mutation, les résultats étant répartis sur deux tableaux. Utilisez la console DynamoDB pour vérifier que les données apparaissent à la fois dans les `locationReadings` tables et. `temperatureReadings`

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

De même, nous devrions également être en mesure de supprimer des lots de relevés de capteurs. Nous allons utiliser le champ GraphQL `Mutation.deleteReadings` à cet effet. Sur la page **Schéma** de la AWS AppSync console, sélectionnez **Joindre** à côté du `Mutation.deleteReadings` champ. Sur l'écran suivant, créez votre résolveur en utilisant le `locationReadings` tableau comme source de données.

Après avoir créé votre résolveur, remplacez les gestionnaires de l'éditeur de code par l'extrait ci-dessous. Dans ce résolveur, nous utilisons un mappeur de fonctions auxiliaires qui extrait le `sensorId` et `timestamp` à partir des entrées fournies. 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const mapper = ({ sensorId, timestamp }) => util.dynamodb.toMapValues({ sensorId, timestamp })

	return {
		operation: 'BatchDeleteItem',
		tables: {
			locationReadings: locReadings.map(mapper),
			temperatureReadings: tempReadings.map(mapper),
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return ctx.result.data
}
```

Enregistrez le résolveur et accédez à la page **Requêtes** de la AWS AppSync console. Supprimons maintenant quelques mesures du capteur.

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

**Note**  
Contrairement à l'opération `DeleteItem`, l'élément complètement supprimé n'est pas renvoyé dans la réponse. Seule la clé passée est renvoyée. Pour en savoir plus, consultez la [référence de la fonction BatchDeleteItem in JavaScript resolver pour DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-batch-delete-item).

Vérifiez via la console DynamoDB que ces deux lectures ont été supprimées `locationReadings` des tables et. `temperatureReadings`

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

Une autre opération courante de notre application consiste à récupérer les mesures d'un capteur à un moment précis. Nous allons joindre un résolveur au champ GraphQL `Query.getReadings` dans notre schéma. Sur la page **Schéma** de la AWS AppSync console, sélectionnez **Joindre** à côté du `Query.getReadings` champ. Sur l'écran suivant, créez votre résolveur en utilisant le `locationReadings` tableau comme source de données.

Utilisons le code suivant : 

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const keys = [util.dynamodb.toMapValues(ctx.args)]
	const consistentRead = true
	return {
		operation: 'BatchGetItem',
		tables: {
			locationReadings: { keys, consistentRead },
			temperatureReadings: { keys, consistentRead },
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	const { locationReadings: locs, temperatureReadings: temps } = ctx.result.data

	return [
		...locs.map((l) => ({ ...l, __typename: 'LocationReading' })),
		...temps.map((t) => ({ ...t, __typename: 'TemperatureReading' })),
	]
}
```

Enregistrez le résolveur et accédez à la page **Requêtes** de la AWS AppSync console. Maintenant, récupérons les relevés de nos 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 à l'aide de. AWS AppSync

## Gestion des erreurs
<a name="error-handling-js"></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. La gestion des erreurs étant intrinsèquement spécifique à l'application, AWS AppSync vous avez la possibilité de gérer les erreurs dans le gestionnaire de réponses. 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`. Dans le gestionnaire de réponses, vous pouvez gérer les résultats partiels de trois manières :

1. Avalez l'erreur d'invocation en renvoyant simplement des données.

1. Déclenchez une erreur (en utilisant`util.error(...)`) en arrêtant l'évaluation du gestionnaire, qui ne renverra aucune donnée.

1. Ajoutez une erreur (en utilisant`util.appendError(...)`) et renvoyez également des données.

Démontrons chacun des trois points ci-dessus avec les opérations par lots DynamoDB.

### Opérations par lots DynamoDB
<a name="dynamodb-batch-operations-js"></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 déclenché `ProvisionedThroughputExceededException` un lors de la deuxième tentative pour traiter AWS AppSync les éléments restants du lot.

Le JSON suivant représente le contexte sérialisé après l'appel par lots DynamoDB mais avant l'appel du gestionnaire de réponses :

```
{
  "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'invocation a été définie sur le contexte et AWS AppSync le type d'erreur a été défini sur. `ctx.error` `DynamoDB:ProvisionedThroughputExceededException`
+ Les résultats sont mappés par table ci-dessous `ctx.result.data` même en cas d'erreur.
+ Les clés non traitées sont disponibles sur`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.

**Note**  
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-js"></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 code que nous écrivons est familier et se concentre uniquement sur les données de résultat.

**Gestionnaire de réponses**

```
export function response(ctx) {
  return 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. Génération d'une erreur pour annuler l'exécution du gestionnaire de réponses
<a name="raising-an-error-to-abort-the-response-execution-js"></a>

Lorsque les échecs partiels doivent être traités comme des échecs complets du point de vue du client, vous pouvez interrompre l'exécution du gestionnaire de réponses pour empêcher le renvoi de données. La méthode d'utilitaire `util.error(...)` permet d'obtenir exactement ce comportement.

**Code du gestionnaire de réponses**

```
export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return 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-js"></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. `util.appendError(...)`Il s'agit de la méthode utilitaire qui permet ce comportement en permettant au concepteur de l'application d'ajouter des erreurs au contexte sans interférer avec l'évaluation du gestionnaire de réponses. Après avoir évalué le gestionnaire de réponse, AWS AppSync traitera toutes les erreurs de contexte en les ajoutant au bloc d'erreurs de la réponse GraphQL.

**Code du gestionnaire de réponses**

```
export function response(ctx) {
  if (ctx.error) {
    util.appendError(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return ctx.result.data;
}
```

Nous avons transmis à la fois l'erreur d'invocation et l'`unprocessedKeys`élément contenu dans le bloc d'erreurs de la réponse GraphQL. Le `getReadings` champ renvoie également des données partielles de la `locationReadings` table, 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. (...)"
    }
  ]
}
```

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

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 .

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

1. Ouvrez la AWS AppSync console et choisissez **Create API**.

1. Choisissez **GraphQL**, APIs puis choisissez **Design from scratch**. Choisissez **Suivant**.

1. Pour le nom de l'API, saisissez `UserData`. Choisissez **Suivant**.

1. Sélectionnez `Create GraphQL resources later`. Choisissez **Suivant**.

1. Passez en revue vos entrées et choisissez **Create API**.

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 davantage votre API GraphQL et exécuter des requêtes.

## 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, utilisez l'extrait ci-dessous :

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

## Configuration de 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 :

1. Sur la page **Sources de données** de votre API AWS AppSync GraphQL, choisissez **Create data** source.

1. Entrez un nom pour la source de données, par exemple`HTTP_Example`.

1. Dans **Type de source de données**, choisissez le point de **terminaison HTTP**.

1. Définissez le point de terminaison sur le point de terminaison API Gateway créé au début du didacticiel. **Vous pouvez trouver votre point de terminaison généré par une pile en accédant à la console Lambda et en trouvant votre application sous Applications.** Dans les paramètres de votre application, vous devriez voir un point de **terminaison d'API** qui sera votre point de terminaison AWS AppSync. Assurez-vous de ne pas inclure le nom de l'étape dans le point de terminaison. Par exemple, si votre point de terminaison l'était`https://aaabbbcccd.execute-api.us-east-1.amazonaws.com/v1`, vous le saisiriez`https://aaabbbcccd.execute-api.us-east-1.amazonaws.com`.

**Note**  
Pour le moment, seuls les points de terminaison publics sont pris en charge par AWS AppSync.  
Pour plus d'informations sur les autorités de certification reconnues par le AWS AppSync service, consultez la section [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 aux `addUser` requêtes `getUser` et.

Pour configurer le `getUser` résolveur, procédez comme suit :

1. Dans votre API AWS AppSync GraphQL, choisissez l'onglet **Schéma**.

1. À droite de l'éditeur de **schéma**, dans le volet **Résolveurs** et sous le type de **requête**, recherchez le `getUser` champ et choisissez **Attacher**.

1. Conservez le type de résolveur à `Unit` et le moteur d'exécution à`APPSYNC_JS`.

1. Dans **Nom de la source de données**, choisissez le point de terminaison HTTP que vous avez créé précédemment.

1. Choisissez **Créer**.

1. Dans l'éditeur de code **Resolver**, ajoutez l'extrait suivant en tant que gestionnaire de requêtes :

   ```
   import { util } from '@aws-appsync/utils'
   
   export function request(ctx) {
   	return {
   		version: '2018-05-29',
   		method: 'GET',
   		params: {
   			headers: {
   				'Content-Type': 'application/json',
   			},
   		},
   		resourcePath: `/v1/users/${ctx.args.id}`,
   	}
   }
   ```

1. Ajoutez l'extrait suivant en tant que gestionnaire de réponses :

   ```
   export function response(ctx) {
   	const { statusCode, body } = ctx.result
   	// if response is 200, return the response
   	if (statusCode === 200) {
   		return JSON.parse(body)
   	}
   	// if response is not 200, append the response to error block.
   	util.appendError(body, statusCode)
   }
   ```

1. 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"
           }
       }
   }
   ```

Pour configurer le `addUser` résolveur, procédez comme suit :

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

1. À droite de l'éditeur de **schéma**, dans le volet **Résolveurs** et sous le type de **requête**, recherchez le `addUser` champ et choisissez **Attacher**.

1. Conservez le type de résolveur à `Unit` et le moteur d'exécution à`APPSYNC_JS`.

1. Dans **Nom de la source de données**, choisissez le point de terminaison HTTP que vous avez créé précédemment.

1. Choisissez **Créer**.

1. Dans l'éditeur de code **Resolver**, ajoutez l'extrait suivant en tant que gestionnaire de requêtes :

   ```
   export function request(ctx) {
       return {
           "version": "2018-05-29",
           "method": "POST",
           "resourcePath": "/v1/users",
           "params":{
               "headers":{
                   "Content-Type": "application/json"
               },
           "body": ctx.args.userInput
           }
       }
   }
   ```

1. Ajoutez l'extrait suivant en tant que gestionnaire de réponses :

   ```
   export function response(ctx) {
       if(ctx.error) {
           return util.error(ctx.error.message, ctx.error.type)
       }
       if (ctx.result.statusCode == 200) {
           return ctx.result.body
       } else {
           return util.appendError(ctx.result.body, "ctx.result.statusCode")
       }
   }
   ```

1. Choisissez l'onglet **Requête** et exécutez la requête suivante :

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

   Si vous réexécutez la `getUser` requête, elle devrait renvoyer la réponse suivante :

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

## Invoquer AWS des services
<a name="invoking-aws-services-js"></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 PostgreSQL avec l'API de données dans AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Découvrez comment connecter votre API GraphQL aux bases de données Aurora PostgreSQL à l'aide de. AWS AppSync Cette intégration vous permet de créer des applications évolutives pilotées par les données en exécutant des requêtes SQL et des mutations via des opérations GraphQL. AWS AppSync fournit une source de données pour exécuter des instructions SQL sur des clusters Amazon Aurora activés par une API de données. Vous pouvez utiliser des AWS AppSync résolveurs pour exécuter des instructions SQL sur l'API de données à l'aide de requêtes GraphQL, de mutations et d'abonnements.

Avant de commencer ce didacticiel, vous devez avoir une connaissance de base des AWS services et des concepts GraphQL.

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

**Topics**
+ [Configuration de votre base de données Aurora PostgreSQL](#creating-clusters)
+ [Création de la base de données et de la table](#creating-db-table)
+ [Création d'un schéma GraphQL](#rds-graphql-schema)
+ [Résolveurs pour RDS](#rds-resolvers)
+ [Supprimer votre cluster](#rds-delete-cluster)

## Configuration de votre base de données Aurora PostgreSQL
<a name="creating-clusters"></a>

Avant d'ajouter une source de données Amazon RDS à AWS AppSync, procédez comme suit.

1. Activez une API de données sur un cluster Aurora Serverless v2.

1. Configurez un secret à l'aide de AWS Secrets Manager

1. Créez le cluster à l'aide de la AWS CLI commande suivante.

   ```
   aws rds create-db-cluster \
               --db-cluster-identifier appsync-tutorial \
               --engine aurora-postgresql \
               --engine-version 16.6 \
               --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éé un cluster, vous devez ajouter une instance Serverless v2 à l'aide de la AWS CLI 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-postgresql
```

**Note**  
Ces points de terminaison mettent du temps à s'activer. Vous pouvez vérifier leur statut dans la console RDS dans l'onglet **Connectivité et sécurité** du cluster.

Vérifiez l'état du cluster à l'aide de la AWS CLI commande suivante.

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

Créez un secret via 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 appsync-tutorial-rds-secret \
    --secret-string file://creds.json
```

Cela renverra un ARN pour le secret. **Prenez note** de l'ARN de votre cluster Aurora Serverless v2 et de Secret pour plus tard lors de la création d'une source de données dans la AWS AppSync console. 

## Création de la base de données et de la table
<a name="creating-db-table"></a>

Créez d'abord une base de données nommée`TESTDB`. Dans PostgreSQL, une base de données est un conteneur qui contient des tables et d'autres objets SQL. Vérifiez 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 *TESTDB* avec le `--sql` paramètre suivant.

```
aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --sql "create DATABASE \"testdb\"" \
    --database "postgres"
```

 Si cela fonctionne sans erreur, ajoutez deux tables avec la `create table` commande :

```
 aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --database "testdb" \
    --sql 'create table public.todos (id serial constraint todos_pk primary key, description text not null, due date not null, "createdAt" timestamp default now());'

aws rds-data execute-statement \
    --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \
    --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \
    --database "testdb" \
    --sql 'create table public.tasks (id serial constraint tasks_pk primary key, description varchar, "todoId" integer not null constraint tasks_todos_id_fk references public.todos);'
```

En cas de succès, ajoutez le cluster en tant que source de données dans votre API.

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

Maintenant que votre API de données Aurora Serverless v2 s'exécute avec des tables configurées, nous allons créer un schéma GraphQL. Vous pouvez créer rapidement votre API en important des configurations de tables depuis une base de données existante à l'aide de l'assistant de création d'API.

Pour commencer : 

1. Dans la AWS AppSync console, choisissez **Create API**, puis **Start with a Amazon Aurora cluster**. 

1. Spécifiez les détails de **l'API, tels que le nom** de l'API, puis sélectionnez votre base de données pour générer l'API.

1. Choisissez votre base de données. Si nécessaire, mettez à jour la région, puis choisissez votre cluster Aurora et votre base de données *TESTDB*. 

1. Choisissez votre secret, puis choisissez **Importer**. 

1. Une fois les tables découvertes, mettez à jour les noms des types. Changez `Todos` vers `Todo` et `Tasks` vers`Task`. 

1. Prévisualisez le schéma généré en choisissant **Aperçu du schéma**. Votre schéma ressemblera à ceci : 

   ```
   type Todo {
     id: Int!
     description: String!
     due: AWSDate!
     createdAt: String
   }
   
   type Task {
     id: Int!
     todoId: Int!
     description: String
   }
   ```

1. Pour le rôle, vous pouvez soit AWS 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:ExecuteStatement"
               ],
               "Resource": [
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial",
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial:*"
               ]
           },
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": [
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret",
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret:*"
               ]
           }
       ]
   }
   ```

------

   Notez que cette politique contient deux déclarations auxquelles vous accordez un accès aux rôles. La première ressource est votre cluster Aurora et la seconde est votre AWS Secrets Manager ARN. 

   Choisissez **Next**, passez en revue les détails de configuration, puis choisissez **Create API**. Vous disposez désormais d'une API entièrement opérationnelle. Vous pouvez consulter tous les détails de votre API sur la page **Schéma**. 

## Résolveurs pour RDS
<a name="rds-resolvers"></a>

Le flux de création d'API a automatiquement créé les résolveurs pour interagir avec nos types. Si vous regardez la page **Schéma**, vous trouverez des résolveurs parmi les résolveurs suivants.
+ Créez un `todo` via le `Mutation.createTodo` champ.
+ Mettez à jour un `todo` via le `Mutation.updateTodo` champ.
+ Supprimez un `todo` via le `Mutation.deleteTodo` champ.
+ Obtenez-en un `todo` sur le `Query.getTodo` terrain.
+ `todos`Répertoriez tout via le `Query.listTodos` champ.

Vous trouverez des champs et des résolveurs similaires attachés au `Task` type. Examinons de plus près certains des résolveurs. 

### Mutation.CreateToDo
<a name="createtodo"></a>

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez à `testdb` côté de`createTodo(...): Todo`. Le code du résolveur utilise la `insert` fonction du `rds` module pour créer dynamiquement une instruction d'insertion qui ajoute des données à la `todos` table. Comme nous travaillons avec Postgres, nous pouvons tirer parti de `returning` cette instruction pour récupérer les données insérées.

Mettez à jour le résolveur suivant pour spécifier correctement le `DATE` type du `due` champ.

```
import { util } from '@aws-appsync/utils';
import { insert, createPgStatement, toJsonObject, typeHint } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input } = ctx.args;
    // if a due date is provided, cast is as `DATE`
    if (input.due) {
        input.due = typeHint.DATE(input.due)
    }
    const insertStatement = insert({
        table: 'todos',
        values: input,
        returning: '*',
    });
    return createPgStatement(insertStatement)
}

export function response(ctx) {
    const { error, result } = ctx;
    if (error) {
        return util.appendError(
            error.message,
            error.type,
            result
        )
    }
    return toJsonObject(result)[0][0]
}
```

Enregistrez le résolveur. L'indice de type marque le type `due` correct dans notre objet d'entrée en tant que `DATE` type. Cela permet au moteur Postgres d'interpréter correctement la valeur. Ensuite, mettez à jour votre schéma pour le supprimer `id` de l'`CreateTodo`entrée. Notre base de données Postgres pouvant renvoyer l'identifiant généré, vous pouvez vous y fier pour la création et le renvoi du résultat sous forme de requête unique, comme suit.

```
input CreateTodoInput {
    due: AWSDate!
    createdAt: String
    description: String!
}
```

Apportez la modification et mettez à jour votre schéma. Accédez à l'éditeur de **requêtes** pour ajouter un élément à la base de données comme suit.

```
mutation CreateTodo {
  createTodo(input: {description: "Hello World!", due: "2023-12-31"}) {
    id
    due
    description
    createdAt
  }
}
```

Vous obtenez le résultat suivant.

```
{
  "data": {
    "createTodo": {
      "id": 1,
      "due": "2023-12-31",
      "description": "Hello World!",
      "createdAt": "2023-11-14 20:47:11.875428"
    }
  }
}
```

### Query.ListToDos
<a name="listtodo"></a>

Dans l'éditeur de schéma de la console, sur le côté droit, choisissez à `testdb` côté de`listTodos(id: ID!): Todo`. Le gestionnaire de demandes utilise la fonction utilitaire de sélection pour créer une demande de manière dynamique au moment de l'exécution.

```
export function request(ctx) {
    const { filter = {}, limit = 100, nextToken } = ctx.args;
    const offset = nextToken ? +util.base64Decode(nextToken) : 0;
    const statement = select({
        table: 'todos',
        columns: '*',
        limit,
        offset,
        where: filter,
    });
    return createPgStatement(statement)
}
```

Nous voulons filtrer `todos` en fonction de la `due` date. Mettons à jour le résolveur vers lequel convertir `due` `DATE` les valeurs. Mettez à jour la liste des importations et le gestionnaire de demandes comme suit.

```
import { util } from '@aws-appsync/utils';
import * as rds from '@aws-appsync/utils/rds';

export function request(ctx) {
  const { filter: where = {}, limit = 100, nextToken } = ctx.args;
  const offset = nextToken ? +util.base64Decode(nextToken) : 0;

  // if `due` is used in a filter, CAST the values to DATE.
  if (where.due) {
    Object.entries(where.due).forEach(([k, v]) => {
      if (k === 'between') {
        where.due[k] = v.map((d) => rds.typeHint.DATE(d));
      } else {
        where.due[k] = rds.typeHint.DATE(v);
      }
    });
  }

  const statement = rds.select({
    table: 'todos',
    columns: '*',
    limit,
    offset,
    where,
  });
  return rds.createPgStatement(statement);
}

export function response(ctx) {
  const {
    args: { limit = 100, nextToken },
    error,
    result,
  } = ctx;
  if (error) {
    return util.appendError(error.message, error.type, result);
  }
  const offset = nextToken ? +util.base64Decode(nextToken) : 0;
  const items = rds.toJsonObject(result)[0];
  const endOfResults = items?.length < limit;
  const token = endOfResults ? null : util.base64Encode(`${offset + limit}`);
  return { items, nextToken: token };
}
```

Dans l'éditeur de **requêtes**, procédez comme suit.

```
query LIST {
  listTodos(limit: 10, filter: {due: {between: ["2021-01-01", "2025-01-02"]}}) {
    items {
      id
      due
      description
    }
  }
}
```

### Mutation. Mise à jour à faire
<a name="updatetodo"></a>

Vous pouvez également `update` un`Todo`. Dans l'éditeur de **requêtes**, mettons à jour notre premier `Todo` élément de `id``1`.

```
mutation UPDATE {
  updateTodo(input: {id: 1, description: "edits"}) {
    description
    due
    id
  }
}
```

Notez que vous devez spécifier `id` l'élément que vous mettez à jour. Vous pouvez également spécifier une condition pour ne mettre à jour qu'un élément répondant à des conditions spécifiques. Par exemple, il se peut que nous souhaitions modifier l'article uniquement si la description commence `edits` comme suit.

```
mutation UPDATE {
  updateTodo(input: {id: 1, description: "edits: make a change"}, condition: {description: {beginsWith: "edits"}}) {
    description
    due
    id
  }
}
```

Tout comme nous avons géré nos `create` `list` opérations, nous pouvons mettre à jour notre résolveur pour transformer le `due` champ en un`DATE`. Enregistrez ces modifications `updateTodo` comme suit.

```
import { util } from '@aws-appsync/utils';
import * as rds from '@aws-appsync/utils/rds';

export function request(ctx) {
  const { input: { id, ...values }, condition = {}, } = ctx.args;
  const where = { ...condition, id: { eq: id } };

  // if `due` is used in a condition, CAST the values to DATE.
  if (condition.due) {
    Object.entries(condition.due).forEach(([k, v]) => {
      if (k === 'between') {
        condition.due[k] = v.map((d) => rds.typeHint.DATE(d));
      } else {
        condition.due[k] = rds.typeHint.DATE(v);
      }
    });
  }

  // if a due date is provided, cast is as `DATE`
  if (values.due) {
    values.due = rds.typeHint.DATE(values.due);
  }

  const updateStatement = rds.update({
    table: 'todos',
    values,
    where,
    returning: '*',
  });
  return rds.createPgStatement(updateStatement);
}

export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    return util.appendError(error.message, error.type, result);
  }
  return rds.toJsonObject(result)[0][0];
}
```

Maintenant, essayez une mise à jour avec une condition :

```
mutation UPDATE {
  updateTodo(
    input: {
        id: 1, description: "edits: make a change", due: "2023-12-12"},
    condition: {
        description: {beginsWith: "edits"}, due: {ge: "2023-11-08"}})
    {
          description
          due
          id
        }
}
```

### Mutation.DeleteToDo
<a name="deletetodo"></a>

Vous pouvez `delete` `Todo` utiliser la `deleteTodo` mutation. Cela fonctionne comme la `updateTodo` mutation, et vous devez spécifier `id` l'élément que vous souhaitez supprimer comme suit.

```
mutation DELETE {
  deleteTodo(input: {id: 1}) {
    description
    due
    id
  }
}
```

### Rédaction de requêtes personnalisées
<a name="writing-custom-queries"></a>

Nous avons utilisé les utilitaires du `rds` module pour créer nos instructions SQL. Nous pouvons également écrire notre propre déclaration statique personnalisée pour interagir avec notre base de données. Commencez par mettre à jour le schéma pour supprimer le `id` champ de l'`CreateTask`entrée.

```
input CreateTaskInput {
    todoId: Int!
    description: String
}
```

Créez ensuite quelques tâches. Une tâche possède une relation de clé étrangère `Todo` comme suit.

```
mutation TASKS {
  a: createTask(input: {todoId: 2, description: "my first sub task"}) { id }
  b:createTask(input: {todoId: 2, description: "another sub task"}) { id }
  c: createTask(input: {todoId: 2, description: "a final sub task"}) { id }
}
```

Créez un nouveau champ de votre `Query` type appelé `getTodoAndTasks` comme suit.

```
getTodoAndTasks(id: Int!): Todo
```

Ajoutez un `tasks` champ au `Todo` type comme suit.

```
type Todo {
    due: AWSDate!
    id: Int!
    createdAt: String
    description: String!
    tasks:TaskConnection
}
```

Enregistrez le schéma. Dans l'éditeur de schéma de la console, sur le côté droit, choisissez **Attach Resolver** for`getTodosAndTasks(id: Int!): Todo`. Choisissez votre source de données Amazon RDS. Mettez à jour votre résolveur avec le code suivant.

```
import { sql, createPgStatement,toJsonObject } from '@aws-appsync/utils/rds';

export function request(ctx) {
    return createPgStatement(
        sql`SELECT * from todos where id = ${ctx.args.id}`,
        sql`SELECT * from tasks where "todoId" = ${ctx.args.id}`);
}

export function response(ctx) {
    const result = toJsonObject(ctx.result);
    const todo = result[0][0];
    if (!todo) {
        return null;
    }
    todo.tasks = { items: result[1] };
    return todo;
}
```

Dans ce code, nous utilisons le modèle de `sql` balise pour écrire une instruction SQL à laquelle nous pouvons transmettre une valeur dynamique en toute sécurité lors de l'exécution. `createPgStatement`peut traiter jusqu'à deux requêtes SQL à la fois. Nous l'utilisons pour envoyer une requête pour notre `todo` et une autre pour notre`tasks`. Vous auriez pu le faire avec une `JOIN` déclaration ou toute autre méthode d'ailleurs. L'idée est de pouvoir écrire votre propre instruction SQL pour implémenter votre logique métier. Pour utiliser la requête dans l'éditeur de **requêtes**, procédez comme suit.

```
query TodoAndTasks {
  getTodosAndTasks(id: 2) {
    id
    due
    description
    tasks {
      items {
        id
        description
      }
    }
  }
}
```

## Supprimer votre cluster
<a name="rds-delete-cluster"></a>

**Important**  
La suppression d'un cluster est définitive. Passez en revue votre projet de manière approfondie avant de réaliser cette action.

Pour supprimer votre cluster :

```
$ aws rds delete-db-cluster \
    --db-cluster-identifier appsync-tutorial \
    --skip-final-snapshot
```