

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.

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

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

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

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

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

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

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

****  

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

------

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

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

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

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

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

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

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

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

input PostInput {
    id: ID!
    title: String
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Nous allons utiliser le schéma GraphQL suivant :

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

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

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

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

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

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

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

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

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

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

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

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

 **Modèle de mappage de demande** 

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

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

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

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

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

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

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

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

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

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

Exécutez la mutation suivante :

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

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

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

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

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

 **Modèle de mappage de demande** 

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

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

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

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

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

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

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

Exécutez la mutation suivante :

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

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

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

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

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

 **Modèle de mappage de demande** 

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

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

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

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

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

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

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

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

$util.toJson($sensorReadings)
```

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

Exécutez la requête suivante :

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Modèle de mappage de réponse :

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

Réponse GraphQL :

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

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

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

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

Modèle de mappage de réponse :

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

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

Réponse GraphQL :

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

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

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

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

Modèle de mappage de réponse :

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

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

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

Réponse GraphQL :

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