

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Usando operações em lote do DynamoDB no AWS AppSync
<a name="tutorial-dynamodb-batch"></a>

**nota**  
Agora, oferecemos suporte principalmente ao runtime do APPSYNC\$1JS e sua documentação. Considere usar o runtime do APPSYNC\$1JS e seus guias disponíveis [aqui](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html).

AWS AppSync suporta o uso de operações em lote do Amazon DynamoDB em uma ou mais tabelas em uma única região. As operações compatíveis são `BatchGetItem`, `BatchPutItem` e `BatchDeleteItem`. Ao usar esses recursos no AWS AppSync, execute tarefas como:
+ Enviar uma lista de chaves em uma única consulta e retornar os resultados de uma tabela
+ Ler os registros de uma ou mais tabelas em uma única consulta
+ Gravar registros em lote em uma ou mais tabelas
+ Gravar ou excluir registros condicionalmente em várias tabelas que podem ter uma relação

Usar operações em lote com o DynamoDB AWS AppSync in é uma técnica avançada que exige um pouco mais de reflexão e conhecimento sobre suas operações de back-end e estruturas de tabelas. Além disso, as operações em lote AWS AppSync têm duas diferenças principais em relação às operações sem lote:
+ A função da fonte de dados deve ter permissões para todas as tabelas acessadas pelo resolvedor.
+ A especificação de tabela para um resolvedor faz parte do modelo de mapeamento.

## Permissões
<a name="permissions"></a>

Como outros resolvedores, você precisa criar uma fonte de dados AWS AppSync e criar uma função ou usar uma existente. Como operações em lote exigem diferentes permissões em tabelas do DynamoDB, é necessário conceder as permissões de função configuradas para ações de leitura e gravação:

------
#### [ 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/*"
            ]
        }
    ]
}
```

------

 **Observação**: as funções estão vinculadas às fontes de dados em AWS AppSync, e os resolvedores nos campos são invocados em relação a uma fonte de dados. As fontes de dados configuradas para buscar no DynamoDB têm apenas uma tabela especificada, para manter a configuração simples. Portanto, ao executar uma operação em lote para várias tabelas em um único resolvedor, que é uma tarefa mais avançada, é necessário conceder acesso à função na fonte de dados para qualquer tabela com a qual o resolvedor interage. Isso é feito no campo **Recurso** na política do IAM acima. A configuração das tabelas para realizar chamadas de lote é feita no modelo de resolvedor, descrita abaixo.

## Fonte de dados
<a name="data-source"></a>

Para simplificar, usaremos a mesma fonte de dados para todos os resolvedores usados neste tutorial. Na guia **Fontes de dados**, crie uma nova fonte de dados do DynamoDB e nomeie-a. **BatchTutorial** O nome da tabela pode ser qualquer coisa pois os nomes de tabelas são especificados como parte do modelo de mapeamento da solicitação para operações em lote. Chamaremos a tabela de `empty`.

Para esse tutorial, qualquer função com a seguinte política em linha funcionará:

## Lote de tabela única
<a name="single-table-batch"></a>

**Atenção**  
`BatchPutItem` e `BatchDeleteItem` não são compatíveis quando usados com detecção e resolução de conflitos. Essas configurações devem ser desativadas para evitar possíveis erros.

Para este exemplo, digamos que tenha uma única tabela chamada **Publicações** à qual você deseja adicionar e remover itens com operações em lote. Use o esquema a seguir, observando que, para a consulta, passaremos uma lista 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
}
```

Anexe um resolvedor ao campo `batchAdd()` com o seguinte **Modelo de mapeamento da solicitação**. Isso recebe automaticamente cada item no tipo `input PostInput` do GraphQL e cria um mapa, que é necessário para a operação `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)
    }
}
```

Nesse caso, o **Modelo de mapeamento da resposta** é uma simples passagem, mas observe que o nome da tabela está anexado como `..data.Posts` ao objeto de contexto:

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

Agora, navegue até a página **Queries (Consultas)** do console do AWS AppSync e execute a seguinte mutação **batchAdd**:

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

Veja os resultados impressos na tela e valide-os de forma independente por meio do console do DynamoDB que ambos os valores gravaram na tabela **Publicações**.

Em seguida, anexe um resolvedor ao campo `batchGet()` com o seguinte **Modelo de mapeamento da solicitação**. Isso recebe automaticamente cada item no tipo `ids:[]` do GraphQL e cria um mapa, que é necessário para a operação `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"}
                }
        }
    }
}
```

O **Modelo de mapeamento da resposta** é novamente uma simples passagem, de novo com o nome da tabela anexado como `..data.Posts` ao objeto de contexto:

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

Agora volte à página **Queries (Consultas)** do console do AWS AppSync e execute a seguinte **consulta batchGet**:

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

Isso deve retornar os resultados para os dois valores `id` adicionados anteriormente. Observe que um valor `null` foi retornado para o `id` com um valor de `3`. Isso ocorre porque não havia registro na tabela **Publicações** com esse valor ainda. Observe também que AWS AppSync retorna os resultados na mesma ordem das chaves passadas para a consulta, que é um recurso adicional que AWS AppSync funciona em seu nome. Portanto, se você alternar para `batchGet(ids:[1,3,2)`, verá a ordem alterada. Você também saberá qual `id` retornou um valor `null`.

Finalmente, anexe um resolvedor ao campo `batchDelete()` com o seguinte **Modelo de mapeamento da solicitação**. Isso recebe automaticamente cada item no tipo `ids:[]` do GraphQL e cria um mapa, que é necessário para a operação `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)
    }
}
```

O **Modelo de mapeamento da resposta** é novamente uma simples passagem, de novo com o nome da tabela anexado como `..data.Posts` ao objeto de contexto:

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

Agora volte à página **Queries (Consultas)** do console do AWS AppSync e execute a mutação **batchDelete** a seguir:

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

Os registros com `id`, `1` e `2` devem ser excluídos agora. Se você executar novamente a consulta `batchGet()` de anteriormente, eles devem retornar `null`.

## Lote de várias tabelas
<a name="multi-table-batch"></a>

**Atenção**  
`BatchPutItem` e `BatchDeleteItem` não são compatíveis quando usados com detecção e resolução de conflitos. Essas configurações devem ser desativadas para evitar possíveis erros.

AWS AppSync também permite que você execute operações em lote nas tabelas. Vamos criar um aplicativo mais complexo. Imagine que estamos criando um aplicativo de Saúde do animal de estimação, onde sensores relatam a localização do animal e a temperatura do corpo. Os sensores são alimentados por bateria e tentam se conectar à rede a cada cinco minutos. Quando um sensor estabelece uma conexão, ele envia suas leituras para nossa AWS AppSync API. Em seguida, os gatilhos analisam os dados para que um painel seja apresentado ao dono do animal. Vamos nos concentrar na representação das interações entre o sensor e o armazenamento de dados de back-end.

Como pré-requisito, primeiro vamos criar duas tabelas do DynamoDB; **locationReadings** armazenará as leituras de localização do sensor e **temperatureReadings** armazenará as leituras de temperatura do sensor. Ambas as tabelas compartilham a mesma estrutura de chave primária: `sensorId (String)` sendo a chave de partição e `timestamp (String)` a chave de classificação.

Vamos usar o seguinte esquema do GraphQL:

```
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 - Gravação de leituras do sensor
<a name="batchputitem-recording-sensor-readings"></a>

Nossos sensores precisam ser capazes de enviar suas leituras assim que se conectarem à internet. O campo do GraphQL `Mutation.recordReadings` é a API que será usada para fazer isso. Vamos anexar um resolvedor para dar vida à nossa API.

Selecione **Anexar** ao lado do campo `Mutation.recordReadings`. Na próxima tela, escolha a mesma fonte de dados `BatchTutorial` criada no início do tutorial.

Vamos adicionar o seguinte modelo de mapeamento da solicitação:

 **Modelo de mapeamento da solicitação** 

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

Como você pode ver, a operação `BatchPutItem` nos permite especificar várias tabelas.

Vamos usar o seguinte modelo de mapeamento da resposta.

 **Modelo de mapeamento da resposta** 

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

Com operações em lote, podem haver erros e resultados retornados na invocação. Nesse caso, podemos fazer uma manipulação de erros adicional.

 **Observação**: o uso de `$utils.appendError()` é semelhante ao `$util.error()`, com a principal distinção de que ele não interrompe a avaliação do modelo de mapeamento. Em vez disso, ele sinaliza que ocorreu um erro com o campo, mas permite que o modelo seja avaliado e, consequentemente, retorne dados ao chamador. Recomendamos que você use `$utils.appendError()` quando o aplicativo precisar retornar resultados parciais.

Salve o resolvedor e navegue até a página **Consultas** do AWS AppSync console. Vamos enviar algumas leituras do sensor\$1

Execute a seguinte mutação:

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

Enviamos dez leituras do sensor em uma mutação, com as leituras divididas em duas tabelas. Use o console do DynamoDB para validar que os dados são exibidos nas tabelas **locationReadings** e **temperatureReadings**.

### BatchDeleteItem - Excluindo leituras do sensor
<a name="batchdeleteitem-deleting-sensor-readings"></a>

Da mesma forma, também será necessário excluir lotes de leituras do sensor. Vamos usar o campo do GraphQL `Mutation.deleteReadings` para essa finalidade. Selecione **Anexar** ao lado do campo `Mutation.recordReadings`. Na próxima tela, escolha a mesma fonte de dados `BatchTutorial` criada no início do tutorial.

Vamos usar o seguinte modelo de mapeamento da solicitação.

 **Modelo de mapeamento da solicitação** 

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

O modelo de mapeamento da resposta é o mesmo usado para `Mutation.recordReadings`.

 **Modelo de mapeamento da resposta** 

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

Salve o resolvedor e navegue até a página **Consultas** do AWS AppSync console. Agora, vamos excluir algumas leituras do sensor\$1

Execute a seguinte mutação:

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

Valide por meio do console do DynamoDB que essas duas leituras foram excluídas das tabelas **locationReadings** e **temperatureReadings**.

### BatchGetItem - Recuperar leituras
<a name="batchgetitem-retrieve-readings"></a>

Outra operação comum para o nosso aplicativo de Saúde do animal de estimação seria recuperar as leituras de um sensor em um determinado momento. Vamos anexar um resolvedor ao campo do GraphQL `Query.getReadings` em nosso esquema. Selecione **Anexar**, e na próxima tela escolha a mesma fonte de dados `BatchTutorial` criada no início do tutorial.

Vamos adicionar o seguinte modelo de mapeamento da solicitação.

 **Modelo de mapeamento da solicitação** 

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

Observe que agora estamos usando a **BatchGetItem**operação.

Nosso modelo de mapeamento da resposta será um pouco diferente, pois escolhemos retornar uma lista `SensorReading`. Vamos mapear o resultado da invocação no formato desejado.

 **Modelo de mapeamento da resposta** 

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

Salve o resolvedor e navegue até a página **Consultas** do AWS AppSync console. Agora, vamos recuperar as leituras do sensor\$1

Execute a seguinte consulta:

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

Demonstramos com sucesso o uso das operações em lote do DynamoDB usando. AWS AppSync

## Tratamento de erros
<a name="error-handling"></a>

Em AWS AppSync, às vezes, as operações da fonte de dados podem retornar resultados parciais. Resultados parciais será o termo usado para indicar quando a saída de uma operação for composta por alguns dados e um erro. Como a manipulação de erros é inerentemente específica ao aplicativo, o AWS AppSync oferece a oportunidade de lidar com erros no modelo de mapeamento da resposta. O erro de invocação do resolvedor, se presente, está disponível no contexto como `$ctx.error`. Os erros de invocação sempre incluem uma mensagem e um tipo, acessível como propriedades `$ctx.error.message` e `$ctx.error.type`. Durante a invocação do modelo de mapeamento da resposta, você pode lidar com resultados parciais de três maneiras:

1. engolir o erro de invocação apenas retornando dados

1. gerar um erro (usando `$util.error(...)`) ao interromper a avaliação do modelo de mapeamento da resposta, que não retornará quaisquer dados.

1. anexar um erro (usando `$util.appendError(...)`) e também retornar dados

Vamos demonstrar cada um dos três pontos acima com operações em lote do DynamoDB\$1

### Operações em lote do DynamoDB
<a name="dynamodb-batch-operations"></a>

Com as operações em lote do DynamoDB, é possível que um lote seja concluído parcialmente. Ou seja, é possível que alguns dos itens solicitados ou chaves não seja processados. Se não AWS AppSync for possível concluir um lote, os itens não processados e um erro de invocação serão definidos no contexto.

Vamos implementar a manipulação de erros usando a configuração de campo `Query.getReadings` da operação `BatchGetItem` da seção anterior desse tutorial. Dessa vez, vamos fingir que ao executar o campo `Query.getReadings`, a tabela do DynamoDB `temperatureReadings` esgotou sua throughput provisionada. O DynamoDB gerou **ProvisionedThroughputExceededException**um na segunda tentativa AWS AppSync de processar os elementos restantes no lote.

O seguinte JSON representa o contexto serializado após a invocação de lote do DynamoDB, mas antes da avaliação do modelo de mapeamento da resposta.

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

Algumas observações sobre o contexto:
+ **o erro de invocação foi definido no contexto em `$ctx.error` by AWS AppSync e o tipo de erro foi definido como DynamoDB:. ProvisionedThroughputExceededException**
+ os resultados são mapeados por tabela em `$ctx.result.data`, mesmo que haja um erro presente
+ as chaves não processadas estão disponíveis em `$ctx.result.data.unprocessedKeys`. Aqui, não AWS AppSync foi possível recuperar o item com a chave (sensorId:1, timestamp:2018-02-01T 17:21:05.000 \$1 08:00) devido à taxa de transferência insuficiente da tabela.

 **Observação**: em `BatchPutItem`, é `$ctx.result.data.unprocessedItems`. Em `BatchDeleteItem`, é `$ctx.result.data.unprocessedKeys`.

Vamos lidar com esse erro de três maneiras diferentes.

#### 1. Absorção do erro de invocação
<a name="swallowing-the-invocation-error"></a>

Retornar dados sem manipular o erro de invocação efetivamente absorve o erro, tornando o resultado para o determinado campo do GraphQL sempre bem-sucedido.

O modelo de mapeamento da resposta gravado é familiar e se concentra apenas nos dados do resultado.

Modelo de mapeamento da resposta:

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

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

Nenhum erro será adicionado à resposta do erro uma vez que apenas dados foram modificados.

#### 2. Geração de um erro para abortar a execução do modelo
<a name="raising-an-error-to-abort-the-template-execution"></a>

Quando falhas parciais devem ser tratadas como falhas completas do ponto de vista do cliente, você pode abortar a execução do modelo para evitar o retorno de dados. O método utilitário `$util.error(...)` atinge exatamente esse comportamento.

Modelo de mapeamento da resposta:

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

Resposta do 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. (...)"
    }
  ]
}
```

Embora alguns resultados possam ter sido retornados da operação em lote do DynamoDB, escolhemos gerar um erro tal que o campo do GraphQL `getReadings` seja nulo e o erro seja adicionado ao bloco *erros* da resposta do GraphQL.

#### 3. Anexar um erro para retornar dados e erros
<a name="appending-an-error-to-return-both-data-and-errors"></a>

Em alguns casos, para oferecer uma experiência melhor ao usuário, os aplicativos podem retornar resultados parciais e notificar seus clientes sobre os itens não processados. Os clientes podem decidir implementar uma nova tentativa ou traduzir o erro de volta para o usuário final. O `$util.appendError(...)` é o método utilitário que permite esse comportamento, permitindo que o designer do aplicativo anexe erros no contexto sem interferir na avaliação do modelo. Depois de avaliar o modelo, AWS AppSync processará quaisquer erros de contexto anexando-os ao bloco de erros da resposta do GraphQL.

Modelo de mapeamento da resposta:

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

Encaminhamos o erro de invocação e o elemento unprocessedKeys dentro do bloco de erros da resposta do GraphQL. O campo `getReadings` também retorna dados parciais da tabela **locationReadings** como você pode ver na resposta abaixo.

Resposta do 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. (...)"
    }
  ]
}
```