

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

# Tutoriais do resolvedor VTL para AWS AppSync
<a name="tutorials"></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).

Fontes de dados e resolvedores são usados AWS AppSync para traduzir solicitações do GraphQL e buscar informações de seus recursos. AWS AWS AppSync oferece suporte ao provisionamento automático e às conexões com determinados tipos de fonte de dados. AWS AppSync também oferece suporte AWS Lambda ao Amazon DynamoDB, bancos de dados relacionais (Amazon Aurora Serverless), OpenSearch Amazon Service e endpoints HTTP como fontes de dados. Você pode usar uma API GraphQL com seus AWS recursos existentes ou criar fontes de dados e resolvedores do zero. As seções a seguir têm como objetivo elucidar alguns dos casos de uso mais comuns do GraphQL na forma de tutoriais.

AWS AppSync usa *modelos de mapeamento* escritos em Apache Velocity Template Language (VTL) para resolvedores. Para obter mais informações sobre como usar modelos de mapeamento, consulte a [Referência de modelo de mapeamento do resolvedor](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference). Mais informações sobre como trabalhar com VTL estão disponíveis no [Guia de programação do modelo de mapeamento do resolvedor](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide).

AWS AppSync suporta o provisionamento automático de tabelas do DynamoDB a partir de um esquema do GraphQL, conforme descrito em Provisionar a partir do esquema (opcional) e Iniciar um esquema de amostra. Você também pode importar a partir de uma tabela do DynamoDB existente que criará os esquemas e conectará os resolvedores. Isso está descrito em Importar a partir do Amazon DynamoDB (opcional).

**Topics**
+ [Criação de uma aplicação de postagem simples usando resolvedores do DynamoDB](tutorial-dynamodb-resolvers.md)
+ [Usando AWS Lambda resolvedores](tutorial-lambda-resolvers.md)
+ [Usando resolvedores OpenSearch de serviço](tutorial-elasticsearch-resolvers.md)
+ [Uso de resolvedores locais](tutorial-local-resolvers.md)
+ [Combinação de resolvedores do GraphQL](tutorial-combining-graphql-resolvers.md)
+ [Uso de operações em lote do DynamoDB](tutorial-dynamodb-batch.md)
+ [Execução de transações do DynamoDB](tutorial-dynamodb-transact.md)
+ [Uso de resolvedores HTTP](tutorial-http-resolvers.md)
+ [Uso de resolvedores do Aurora Serverless v2](tutorial-rds-resolvers.md)
+ [Uso de resolvedores de pipeline](tutorial-pipeline-resolvers.md)
+ [Uso de operações de sincronização delta em fontes de dados versionadas](tutorial-delta-sync.md)

# Criação de uma aplicação de postagem simples usando resolvedores do DynamoDB
<a name="tutorial-dynamodb-resolvers"></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).

Este tutorial mostra como você pode trazer suas próprias tabelas do Amazon DynamoDB e conectá-las AWS AppSync a uma API do GraphQL.

Você pode permitir o AWS AppSync provisionamento de recursos do DynamoDB em seu nome. Ou, se preferir, você pode conectar as tabelas existentes a um esquema do GraphQL criando uma fonte de dados e um resolvedor. Em ambos os casos, você poderá ler e gravar no banco de dados do DynamoDB por meio de instruções do GraphQL e assinar dados em tempo real.

Existem etapas de configuração específicas que precisam ser concluídas para que as instruções do GraphQL sejam traduzidas para operações do DynamoDB e para que as respostas sejam traduzidas novamente para o GraphQL. Esse tutorial descreve o processo de configuração por meio de vários cenários reais e padrões de acesso aos dados.

## Configuração de tabelas do DynamoDB
<a name="setting-up-your-ddb-tables"></a>

Para começar este tutorial, primeiro você precisa seguir as etapas abaixo para provisionar AWS recursos.

1. Provisione AWS recursos usando o seguinte AWS CloudFormation modelo na CLI:

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

   Como alternativa, você pode lançar a seguinte CloudFormation pilha na região Oeste dos EUA 2 (Oregon) em sua conta. AWS 

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

   Isso cria o seguinte:
   + Uma tabela do DynamoDB chamada `AppSyncTutorial-Post` que armazenará os dados de `Post`.
   + Uma função do IAM e uma política gerenciada do IAM associada AWS AppSync para permitir a interação com a `Post` tabela.

1. Para ver mais detalhes sobre a pilha e os recursos criados, execute o seguinte comando da CLI:

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

1. Para excluir os recursos mais tarde, execute o seguinte:

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

## Criação da API GraphQL
<a name="creating-your-graphql-api"></a>

Para criar a API GraphQL em: AWS AppSync

1. Faça login no Console de gerenciamento da AWS e abra o [AppSync console](https://console.aws.amazon.com/appsync/).

   1. No **APIs painel**, escolha **Criar API**.

1. Na janela **Personalizar sua API ou importar a partir do Amazon DynamoDB**, escolha **Criar a partir do zero**.

   1. Escolha **Iniciar** à direita da mesma janela.

1. No campo **Nome da API**, defina o nome da API para `AWSAppSyncTutorial`.

1. Escolha **Criar**.

O AWS AppSync console cria uma nova API GraphQL para você usando o modo de autenticação por chave de API. Você pode usar o console para configurar o restante da API GraphQL e executar consultas nela durante o restante desse tutorial.

## Definição de uma API Post básica
<a name="defining-a-basic-post-api"></a>

Agora que você criou uma API AWS AppSync GraphQL, você pode configurar um esquema básico que permite a criação, recuperação e exclusão básicas de dados de postagem.

1. Faça login no Console de gerenciamento da AWS e abra o [AppSync console](https://console.aws.amazon.com/appsync/).

   1. No **APIs painel**, escolha a API que você acabou de criar.

1. Na **barra lateral**, escolha **Esquema**.

   1. No painel **Esquema**, substitua o conteúdo pelo seguinte código:

     ```
     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. Escolha **Salvar**.

Esse esquema define um tipo `Post` e operações para adicionar e obter objetos `Post`.

## Configuração da fonte de dados para as tabelas do DynamoDB
<a name="configuring-the-data-source-for-the-ddb-tables"></a>

Em seguida, vincule as consultas e mutações definidas no esquema à tabela `AppSyncTutorial-Post` do DynamoDB.

Primeiro, AWS AppSync precisa estar atento às suas tabelas. Faça isso ao configurar uma fonte de dados no AWS AppSync:

1. Faça login no Console de gerenciamento da AWS e abra o [AppSync console](https://console.aws.amazon.com/appsync/).

   1. No **APIs painel**, escolha sua API GraphQL.

   1. Na **barra lateral**, escolha **Fontes de dados**.

1. Escolha **Criar fonte de dados**.

   1. Para **Nome da fonte de dados**, insira em `PostDynamoDBTable`. 

   1. Para o **tipo de fonte de dados**, escolha **Tabela do Amazon DynamoDB**.

   1. Para **Região**, escolha **US-WEST-2**.

   1. Em **Nome da tabela**, escolha a tabela **AppSyncTutorial-Post DynamoDB**.

   1. Crie um perfil do IAM (recomendado) ou escolha uma função existente que tenha permissão do IAM `lambda:invokeFunction`. Os perfis existentes precisam de uma política de confiança, conforme explicado na seção [Anexar uma fonte de dados](attaching-a-data-source.md). 

      Veja a seguir um exemplo de política do IAM que tem as permissões necessárias para executar as operações no recurso:

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

****  

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

------

1. Escolha **Criar**.

## Configurando o resolvedor AddPost (DynamoDB) PutItem
<a name="setting-up-the-addpost-resolver-dynamodb-putitem"></a>

**Depois de conhecer AWS AppSync a tabela do DynamoDB, você pode vinculá-la a consultas e mutações individuais definindo Resolvers.** O primeiro resolvedor criado é o resolvedor `addPost`, que permite criar uma publicação na tabela `AppSyncTutorial-Post` do DynamoDB.

Um resolvedor tem os seguintes componentes:
+ A local no esquema do GraphQL para anexar o resolvedor. Nesse caso, você está configurando um resolvedor no campo `addPost` no tipo `Mutation`. Esse resolvedor será invocado quando o chamador chamar `mutation { addPost(...){...} }`.
+ A fonte de dados a ser usada para esse resolvedor. Nesse caso, você deseja usar a fonte de dados `PostDynamoDBTable` definida anteriormente, para que possa adicionar entradas na tabela do DynamoDB `AppSyncTutorial-Post`.
+ O modelo de mapeamento da solicitação. O objetivo do modelo de mapeamento de solicitações é pegar a solicitação recebida do chamador e traduzi-la em instruções AWS AppSync para execução no DynamoDB.
+ O modelo de mapeamento da resposta. O trabalho do modelo de mapeamento da resposta é receber a resposta do DynamoDB e traduzi-la de volta para algo esperado pelo GraphQL. Isso é útil se a forma dos dados no DynamoDB for diferente para o tipo `Post` no GraphQL, mas, nesse caso, elas têm a mesma forma, portanto basta transmitir os dados.

Como configurar o resolvedor :

1. Faça login no Console de gerenciamento da AWS e abra o [AppSync console](https://console.aws.amazon.com/appsync/).

   1. No **APIs painel**, escolha sua API GraphQL.

   1. Na **barra lateral**, escolha **Fontes de dados**.

1. Escolha **Criar fonte de dados**.

   1. Para **Nome da fonte de dados**, insira em `PostDynamoDBTable`. 

   1. Para o **tipo de fonte de dados**, escolha **Tabela do Amazon DynamoDB**.

   1. Para **Região**, escolha **US-WEST-2**.

   1. Em **Nome da tabela**, escolha a tabela **AppSyncTutorial-Post DynamoDB**.

   1. Crie um perfil do IAM (recomendado) ou escolha uma função existente que tenha permissão do IAM `lambda:invokeFunction`. Os perfis existentes precisam de uma política de confiança, conforme explicado na seção [Anexar uma fonte de dados](attaching-a-data-source.md). 

      Veja a seguir um exemplo de política do IAM que tem as permissões necessárias para executar as operações no recurso:

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

****  

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

------

1. Escolha **Criar**.

1. Escolha a guia **Esquema**.

1. No painel **Tipos de dados** à direita, encontre o campo **addPost** no tipo **Mutação** e, em seguida, escolha **Anexar**.

1. No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.

1. Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.

1. Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** um *tipo* é especificado em todas as chaves e valores de atributo. Por exemplo, defina o campo `author` para `{ "S" : "${context.arguments.author}" }`. A `S` parte indica para o DynamoDB AWS AppSync e para o DynamoDB que o valor será um valor de string. O valor real é preenchido a partir do argumento `author`. Da mesma forma, o campo `version` é um campo de número pois ele usa `N` para o tipo. Finalmente, você também está inicializando os campos `ups`, `downs` e `version`.

   Neste tutorial, você especificou que o `ID!` tipo GraphQL, que indexa o novo item inserido no DynamoDB, vem como parte dos argumentos do cliente. AWS AppSync vem com um utilitário para geração automática de ID`$utils.autoId()`, chamado que você também poderia ter usado na forma de`"id" : { "S" : "${$utils.autoId()}" }`. Depois, basta deixar o `id: ID!` fora da definição do esquema de `addPost()` e ele seria inserido automaticamente. Você não usará essa técnica para esse tutorial, mas deve considerá-la como uma das melhores práticas ao gravar em tabelas do DynamoDB.

   Para obter mais informações sobre os modelos de mapeamento, consulte a documentação de referência [Visão geral do modelo de mapeamento do resolvedor](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview). Para obter mais informações sobre mapeamento de GetItem solicitações, consulte a documentação de [GetItem](aws-appsync-resolver-mapping-template-reference-dynamodb-getitem.md)referência. Para obter mais informações sobre os tipos, consulte a documentação de referência [Mapeamento da solicitação](aws-appsync-resolver-mapping-template-reference-dynamodb-typed-values-request.md).

1. Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

    **Observação:** como o formato dos dados na tabela `AppSyncTutorial-Post` corresponde exatamente ao formato do tipo `Post` no GraphQL, o modelo de mapeamento da resposta apenas transmite os resultados diretamente. Observe também que todos os exemplos desse tutorial usam o mesmo modelo de mapeamento da resposta, portanto você só cria um arquivo.

1. Escolha **Salvar**.

### Chamar a API para adicionar uma publicação
<a name="call-the-api-to-add-a-post"></a>

Agora que o resolvedor está configurado, é AWS AppSync possível traduzir uma `addPost` mutação recebida em uma operação do DynamoDB. PutItem Agora você pode executar uma mutação para colocar algo na tabela.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte mutação:

  ```
  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
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Os resultados da publicação recém-criada devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Veja o que aconteceu:
+ AWS AppSync recebeu uma solicitação de `addPost` mutação.
+ AWS AppSync pegou a solicitação e o modelo de mapeamento da solicitação e gerou um documento de mapeamento da solicitação. Isso teria a seguinte aparência:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "PutItem",
      "key" : {
          "id" : { "S" : "123" }
      },
      "attributeValues" : {
          "author": { "S" : "AUTHORNAME" },
          "title": { "S" : "Our first post!" },
          "content": { "S" : "This is our first post." },
          "url": { "S" : "https://aws.amazon.com/appsync/" },
          "ups" : { "N" : 1 },
          "downs" : { "N" : 0 },
          "version" : { "N" : 1 }
      }
  }
  ```
+ AWS AppSync usou o documento de mapeamento da solicitação para gerar e executar uma solicitação do `PutItem` DynamoDB.
+ AWS AppSync pegou os resultados da `PutItem` solicitação e os converteu de volta em tipos 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
  }
  ```
+ Os transmitiu por meio do documento de mapeamento da resposta, sem alterações.
+ Retornou o objeto recém-criado na resposta do GraphQL.

## Configurando o GetPost Resolver (DynamoDB) GetItem
<a name="setting-up-the-getpost-resolver-ddb-getitem"></a>

Agora que você pode adicionar dados à tabela `AppSyncTutorial-Post` do DynamoDB, é necessário configurar a consulta `getPost` para que ela possa recuperar esses dados da tabela `AppSyncTutorial-Post`. Para fazer isso, vamos configurar outro resolvedor.
+ Escolha a guia **Esquema**.
+ No painel **Tipos de dados** à direita, encontre o campo **getPost** no tipo **Consulta** e, em seguida, escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
      }
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.

### Chamar a API para obter uma publicação
<a name="call-the-api-to-get-a-post"></a>

Agora, o resolvedor foi configurado e AWS AppSync sabe como traduzir uma `getPost` consulta recebida em uma operação do DynamoDB. `GetItem` Agora é possível executar uma consulta para recuperar a publicação criada anteriormente.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole o seguinte:

  ```
  query getPost {
    getPost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação recuperada do DynamoDB deve aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Veja o que aconteceu:
+ AWS AppSync recebeu uma solicitação `getPost` de consulta.
+ AWS AppSync pegou a solicitação e o modelo de mapeamento da solicitação e gerou um documento de mapeamento da solicitação. Isso teria a seguinte aparência:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : { "S" : "123" }
      }
  }
  ```
+ AWS AppSync usou o documento de mapeamento da solicitação para gerar e executar uma solicitação do GetItem DynamoDB.
+ AWS AppSync pegou os resultados da `GetItem` solicitação e os converteu de volta para os tipos 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
  }
  ```
+ Os transmitiu por meio do documento de mapeamento da resposta, sem alterações.
+ Retornou o objeto recuperado na resposta.

Como alternativa, utilize o exemplo a seguir:

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

Se sua consulta `getPost` precisar apenas de `id`, `author` e `title`, você poderá alterar seu modelo de mapeamento da solicitação para usar expressões de projeção para especificar apenas os atributos que deseja da tabela do DynamoDB para evitar transferência desnecessária de dados do DynamoDB para AWS AppSync. Por exemplo, o modelo de mapeamento da solicitação pode se parecer com o trecho abaixo:

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

## Criar uma mutação UpdatePost (DynamoDB) UpdateItem
<a name="create-an-updatepost-mutation-ddb-updateitem"></a>

Até agora, você pode criar e recuperar objetos `Post` no DynamoDB. Depois, você configurará uma nova mutação para que possamos atualizar o objeto. Você fará isso usando a operação do UpdateItem DynamoDB.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Mutation` para adicionar uma nova mutação `updatePost` da seguinte forma:

  ```
  type Mutation {
      updatePost(
          id: ID!,
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post
      addPost(
          author: String!
          title: String!
          content: String!
          url: String!
      ): Post!
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **updatePost** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** esse resolvedor está usando o UpdateItem DynamoDB, que é significativamente diferente da operação. PutItem Em vez de escrever o item inteiro, você está apenas pedindo ao DynamoDB para atualizar determinados atributos. Isso é feito usando expressões de atualização do DynamoDB. A expressão em si é especificada no campo `expression` na seção `update`. Ela diz para definir o `author`, o `title`, o `content` e os atributos de url e, em seguida, incrementar o campo `version`. Os valores a serem usados não aparecem na expressão em si; a expressão tem espaços reservados com nomes que começam com "dois pontos", que são definidos no campo `expressionValues`. Finalmente, o DynamoDB tem palavras reservadas que não podem aparecer no `expression`. Por exemplo, `url` é uma palavra reservada, então, para atualizar o campo `url` é possível usar espaços reservados de nome e defini-los no campo `expressionNames`.

  Para obter mais informações sobre mapeamento de `UpdateItem` solicitações, consulte a documentação de [UpdateItem](aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem.md)referência. Para obter mais informações sobre como escrever expressões de atualização, consulte a documentação do [ UpdateExpressions DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

### Chamar a API para atualizar uma publicação
<a name="call-the-api-to-update-a-post"></a>

Agora, o resolvedor foi configurado e AWS AppSync sabe como traduzir uma `update` mutação recebida em uma operação do DynamoDB. `Update` Agora você pode executar uma mutação para atualizar o item escrito anteriormente.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte mutação. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation updatePost {
    updatePost(
      id:"123"
      author: "A new author"
      title: "An updated author!"
      content: "Now with updated content!"
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação atualizada no DynamoDB deve aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Neste exemplo, os `downs` campos `ups` e não foram modificados porque o modelo de mapeamento de solicitações não AWS AppSync solicitou que o DynamoDB fizesse nada com esses campos. Além disso, o `version` campo foi incrementado em 1 porque você pediu ao AWS AppSync DynamoDB que adicionasse 1 ao campo. `version`

## Modificando o UpdatePost Resolver (DynamoDB) UpdateItem
<a name="modifying-the-updatepost-resolver-dynamodb-updateitem"></a>

Esse é um bom início para a mutação `updatePost`, mas tem dois problemas principais:
+ Se quiser atualizar apenas um único campo, é necessário atualizar todos os campos.
+ Se duas pessoas estiverem modificando o objeto, possivelmente haverá perda de informações.

Para resolver esses problemas, você modificará a mutação `updatePost` para modificar apenas os argumentos que foram especificados na solicitação e, depois, adicionará uma condição à operação `UpdateItem`.

1. Escolha a guia **Esquema**.

1. No painel **Esquema**, modifique o campo `updatePost` no tipo `Mutation` para remover os pontos de exclamação dos argumentos `author`, `title`, `content` e `url`, sem se esquecer de deixar o campo `id` como está. Isso os tornará argumento opcional. Além disso, adicione um novo argumento `expectedVersion` obrigatório.

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

1. Escolha **Salvar**.

1. No painel **Tipos de dados**, encontre o campo **updatePost** no tipo **Mutação**.

1. Escolha **PostDynamoDBTable**abrir o resolvedor existente.

1. Em **Configurar o modelo de mapeamento da solicitação**, modifique o modelo de mapeamento da solicitação da seguinte forma:

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

1. Escolha **Salvar**.

Este modelo é um dos exemplos mais complexos. Ele demonstra a potência e a flexibilidade dos modelos de mapeamento. Ele percorre todos os argumentos, ignorando `id` e `expectedVersion`. Se o argumento for definido como algo, ele solicitará que o DynamoDB AWS AppSync atualize esse atributo no objeto no DynamoDB. Se o atributo for definido como nulo, ele AWS AppSync solicitará que o DynamoDB remova esse atributo do objeto de postagem. Se um argumento não foi especificado, nada será feito com o atributo. Ele também incrementa o campo `version`.

Além disso, há uma nova seção `condition`. Uma expressão de condição permite que você diga ao AWS AppSync DynamoDB se a solicitação deve ou não ser bem-sucedida com base no estado do objeto que já está no DynamoDB antes da execução da operação. Nesse caso, você deseja que a solicitação `UpdateItem` seja bem-sucedida apenas se o campo `version` do item atualmente no DynamoDB corresponder ao argumento `expectedVersion`.

Para obter mais informações sobre as expressões de condições, consulte a documentação de referência [Expressões de condições](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md).

### Chamar a API para atualizar uma publicação
<a name="id1"></a>

Vamos tentar atualizar o objeto `Post` com o novo resolvedor:
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation updatePost {
    updatePost(
      id:123
      title: "An empty story"
      content: null
      expectedVersion: 2
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação atualizada no DynamoDB deve aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Nessa solicitação, você pediu ao AWS AppSync DynamoDB que atualizasse somente `title` o campo e. `content` Ele ignorou todos os outros campos (além de incrementar o campo `version`). Definiu-se o atributo `title` para um novo valor e o atributo `content` foi removido da publicação. Os campos `author`, `url`, `ups` e `downs` foram mantidos.

Tente executar a solicitação de mutação novamente, deixando a solicitação exatamente como está. Você verá uma resposta semelhante à seguinte:

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

A solicitação falha pois a expressão de condição é avaliada como falsa:
+ Na primeira vez que você executou a solicitação, o valor do campo `version` da publicação no DynamoDB era `2`, que correspondia ao argumento `expectedVersion`. A solicitação foi bem-sucedida, o que significa que o campo `version` foi incrementado no DynamoDB para `3`.
+ Na segunda vez que a solicitação foi executada, o valor do campo `version` da publicação no DynamoDB era `3`, que não correspondeu ao argumento `expectedVersion`.

Esse padrão é geralmente chamado de *bloqueio positivo*.

Um recurso de um resolvedor do AWS AppSync DynamoDB é que ele retorna o valor atual do objeto post no DynamoDB. Encontre isso no campo `data` na seção `errors` da resposta do GraphQL. O aplicativo pode usar essas informações para decidir como deve continuar. Nesse caso, é possível ver que o campo `version` do objeto no DynamoDB está definido como `3`; portanto, podemos apenas atualizar o argumento `expectedVersion` para `3` e a solicitação teria êxito novamente.

Para obter mais informações sobre o tratamento de falhas da verificação de condição, consulte a documentação de referência do modelo de mapeamento [Expressões de condições](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md).

## Crie mutações UpVotePost e DownVotePost (DynamoDB) UpdateItem
<a name="create-upvotepost-and-downvotepost-mutations-ddb-updateitem"></a>

O tipo `Post` tem campos `ups` e `downs` para habilitar o registro de votos positivos e negativos, mas até agora a API não permite fazer nada com eles. Vamos adicionar algumas mutações para permitir votos positivos e negativos nas publicações.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Mutation` para adicionar novas mutações `upvotePost` e `downvotePost` da seguinte forma:

  ```
  type Mutation {
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **upvotePost** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD ups :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo `downvotePost` recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD downs :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.

### Chamar a API para realizar voto positivo ou negativo em uma Publicação
<a name="call-the-api-to-upvote-and-downvote-a-post"></a>

Agora que os novos resolvedores foram configurados, AWS AppSync sabe como traduzir uma entrada `upvotePost` ou uma `downvote` mutação para a operação do DynamoDB. UpdateItem Agora é possível executar mutações para realizar votos positivos ou negativos na publicação criada anteriormente.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation votePost {
    upvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação é atualizada no DynamoDB e deve aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

  ```
  {
    "data": {
      "upvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 0,
        "version": 4
      }
    }
  }
  ```
+ Escolha **Executar consulta** mais algumas vezes. Você deve ver os campos `ups` e `version` incrementar em 1 a cada vez que você executar a consulta.
+ Altere a consulta para chamar a mutação `downvotePost` da seguinte forma:

  ```
  mutation votePost {
    downvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja). Dessa vez, você deve ver os campos `downs` e `version` incrementar em 1 a cada vez que você executar a consulta.

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

## Configurando o DeletePost Resolver (DynamoDB) DeleteItem
<a name="setting-up-the-deletepost-resolver-ddb-deletepost"></a>

A próxima mutação que você quer configurar é para excluir uma publicação. Você fará isso usando a operação `DeleteItem` do DynamoDB.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Mutation` para adicionar uma nova mutação `deletePost` da seguinte forma:

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

  Dessa vez o campo `expectedVersion` é opcional, o que é explicado mais tarde ao adicionar o modelo de mapeamento da solicitação.
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **delete** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** o argumento `expectedVersion` é opcional. Se o chamador definir um argumento `expectedVersion` na solicitação, o modelo adiciona uma condição que permite que a solicitação `DeleteItem` seja bem-sucedida somente se o item já estiver excluído ou se o atributo `version` da publicação no DynamoDB corresponder exatamente ao `expectedVersion`. Se omitido, nenhuma expressão de condição será especificada na solicitação `DeleteItem`. Ele será bem-sucedida independentemente do valor de `version`, ou se o item existe ou não no DynamoDB.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** mesmo que você esteja excluindo um item, é possível retornar o item que foi excluído, caso ainda não tenha sido excluído.
+ Escolha **Salvar**.

Para obter mais informações sobre mapeamento de `DeleteItem` solicitações, consulte a documentação de [DeleteItem](aws-appsync-resolver-mapping-template-reference-dynamodb-deleteitem.md)referência.

### Chamar a API para excluir uma publicação
<a name="call-the-api-to-delete-a-post"></a>

Agora, o resolvedor foi configurado e AWS AppSync sabe como traduzir uma `delete` mutação recebida em uma operação do DynamoDB. `DeleteItem` Agora você pode executar uma mutação para excluir algo na tabela.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation deletePost {
    deletePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação é excluída do DynamoDB. Observe que AWS AppSync retorna o valor do item que foi excluído do DynamoDB, que deve aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

O valor é retornado somente se essa chamada para `deletePost` foi aquela que realmente a excluiu do DynamoDB.
+ Escolha **Executar consulta** novamente.
+ A chamada ainda será bem-sucedida, mas nenhum valor é retornado.

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

Agora vamos tentar excluir uma publicação, mas dessa vez especificando um `expectedValue`. No entanto, primeiro é necessário criar uma nova publicação, pois você acabou de excluir aquela com a qual estava trabalhando.
+ No painel **Consultas**, cole a seguinte mutação:

  ```
  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
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Os resultados da publicação recém-criada devem aparecer no painel de resultados à direita do painel de consulta. Anote o `id` do objeto recém-criado, pois será necessário em alguns instantes. A aparência deve ser semelhante à seguinte:

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

Agora vamos tentar excluir a publicação, mas coloque o valor errado para `expectedVersion`:
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 9999
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).

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

  A solicitação falhou porque a expressão de condição é avaliada como falsa: o valor para `version` da publicação no DynamoDB não corresponde ao `expectedValue` especificado nos argumentos. O valor atual do objeto é retornada no campo `data` na seção `errors` da resposta do GraphQL.
+ Repita a solicitação, mas corrija o `expectedVersion`:

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 1
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Dessa vez, a solicitação é bem-sucedida e o valor que foi excluído do DynamoDB é retornado:

  ```
  {
    "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
      }
    }
  }
  ```
+ Escolha **Executar consulta** novamente.
+ A chamada ainda é bem-sucedida, mas dessa vez nenhum valor é retornado porque a publicação já estava excluída no DynamoDB.

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

## Configurar o resolvedor allPost (Scan do DynamoDB)
<a name="setting-up-the-allpost-resolver-dynamodb-scan"></a>

Até agora, a API só será útil se você souber o `id` de cada publicação a ser examinada. Vamos adicionar um novo resolvedor que retornará todas as publicações na tabela.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPost` da seguinte forma:

  ```
  type Query {
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```
+ Adicione um novo tipo `PaginationPosts`:

  ```
  type PaginatedPosts {
      posts: [Post!]!
      nextToken: String
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **allPost** recém-criado no tipo **Consulta** e, em seguida, escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

  Esse resolvedor tem dois argumentos opcionais: `count`, que especifica o número máximo de itens que serão retornados em uma única chamada, e `nextToken`, que pode ser usado para recuperar o próximo conjunto de resultados, (você mostrará de onde vem o valor para `nextToken` posteriormente).
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** esse modelo de mapeamento da resposta é diferente de todos os outros até agora. O resultado da consulta `allPost` é um `PaginatedPosts`, que contém uma lista de publicações e um token de paginação. A forma desse objeto é diferente da retornada do AWS AppSync DynamoDB Resolver: a lista de publicações é chamada `items` nos resultados do AWS AppSync DynamoDB Resolver, mas é chamada de entrada. `posts` `PaginatedPosts`
+ Escolha **Salvar**.

Para obter mais informações sobre o mapeamento da solicitação `Scan`, consulte a documentação de referência [Scan](aws-appsync-resolver-mapping-template-reference-dynamodb-scan.md).

### Chamar a API para verificar todas as publicações
<a name="call-the-api-to-scan-all-posts"></a>

Agora, o resolvedor foi configurado e AWS AppSync sabe como traduzir uma `allPost` consulta recebida em uma operação do DynamoDB. `Scan` Agora você pode verificar a tabela para recuperar todas as publicações.

No entanto, antes de testar, é necessário preencher a tabela com alguns dados, pois tudo que foi usado até agora já foi excluído.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte mutação:

  ```
  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 }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).

Agora vamos examinar a tabela, retornando cinco resultados por vez.
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  query allPost {
    allPost(count: 5) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ As primeiras cinco publicações devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Você tem cinco resultados e um `nextToken` que pode ser usado para obter o próximo conjunto de resultados.
+ Atualize a consulta `allPost` para incluir o `nextToken` do conjunto de resultados anterior:

  ```
  query allPost {
    allPost(
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0="
    ) {
      posts {
        id
        author
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ As quatro publicações restantes devem aparecer no painel de resultados à direita do painel de consulta. Não há `nextToken` nesse conjunto de resultados pois todos os nove resultados foram encontrados, sem nenhum restante. A aparência deve ser semelhante à seguinte:

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

## Configurando o allPostsBy Author Resolver (DynamoDB Query)
<a name="setting-up-the-allpostsbyauthor-resolver-ddb-query"></a>

Além de verificar todas as publicações do DynamoDB, também é possível consultar o DynamoDB para recuperar as publicações criadas por um determinado autor. A tabela do DynamoDB criada anteriormente já possui um `GlobalSecondaryIndex` chamado `author-index` que podemos usar com uma operação `Query` do DynamoDB para recuperar todos as publicações criadas por um determinado autor.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPostsByAuthor` da seguinte forma:

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

   **Observação:** isso usa o mesmo tipo `PaginatedPosts` usado com a consulta `allPost`.
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, localize o campo **allPostsByAutor** recém-criado no Tipo de **consulta** e escolha **Anexar**.
+ No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

  Como o resolvedor `allPost`, esse resolvedor tem dois argumentos opcionais: `count`, que especifica o número máximo de itens que serão retornados em uma única chamada e `nextToken`, que pode ser usado para recuperar o próximo conjunto de resultados (o valor para `nextToken` pode ser obtido de uma chamada anterior).
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

   **Observação:** esse é o mesmo modelo de mapeamento da resposta usado no resolvedor `allPost`.
+ Escolha **Salvar**.

Para obter mais informações sobre o mapeamento da solicitação `Query`, consulte a documentação de referência [Consulta](aws-appsync-resolver-mapping-template-reference-dynamodb-query.md).

### Chamar a API para consultar todas as publicações de um autor
<a name="call-the-api-to-query-all-posts-by-an-author"></a>

Agora, o resolvedor foi configurado e AWS AppSync sabe como traduzir uma `allPostsByAuthor` mutação recebida em uma operação do DynamoDB em relação ao índice`Query`. `author-index` Agora você pode consultar a tabela para recuperar todas as publicações de um determinado autor.

Antes de fazer isso, no entanto, vamos preencher a tabela com mais algumas publicações pois até agora todas têm o mesmo autor.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte mutação:

  ```
  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 }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).

Agora, vamos consultar a tabela, retornando todas as publicações de autoria da `Nadia`.
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(author: "Nadia") {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Todas as publicações de autoria da `Nadia` devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

A paginação funciona para `Query` da mesma forma que funciona para `Scan`. Por exemplo, vamos procurar todas as publicações por `AUTHORNAME`, obtendo cinco por vez.
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Todas as publicações de autoria da `AUTHORNAME` devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
      }
    }
  }
  ```
+ Atualize o argumento `nextToken` com o valor retornado pela consulta anterior da seguinte forma:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ As publicações restantes de autoria de `AUTHORNAME` devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

## Uso de conjuntos
<a name="using-sets"></a>

Até esse ponto, o `Post` tipo era um key/value objeto plano. Você também pode modelar objetos complexos com o resolvedor de AWS AppSyncDynamo banco de dados, como conjuntos, listas e mapas.

Vamos atualizar o tipo `Post` para incluir tags. Uma publicação pode ter 0 ou mais tags, que são armazenadas no DynamoDB como um Conjunto de strings. Vamos configurar também algumas mutações para adicionar e remover tags, e uma nova consulta para verificar as publicações com uma tag específica.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, modifique o tipo `Post` para adicionar um novo campo `tags` da seguinte forma:

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
  }
  ```
+ No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPostsByTag` da seguinte forma:

  ```
  type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID): Post
  }
  ```
+ No painel **Esquema**, modifique o tipo `Mutation` para adicionar novas mutações `addTag` e `removeTag` da seguinte forma:

  ```
  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!
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, localize o campo **allPostsByTag** recém-criado no Tipo de **consulta** e escolha **Anexar**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan",
      "filter": {
        "expression": "contains (tags, :tag)",
          "expressionValues": {
            ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **addTag** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD tags :tags, version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **removeTag** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "DELETE tags :tags ADD version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.

### Chamar a API para trabalhar com tags
<a name="call-the-api-to-work-with-tags"></a>

Agora que você configurou os resolvedores, AWS AppSync sabe como traduzir as entradas `addTag` e as `allPostsByTag` solicitações em DynamoDB e operações. `removeTag` `UpdateItem` `Scan`

Para testar, vamos selecionar uma das publicações criadas anteriormente. Por exemplo, vamos usar uma publicação de autoria da `Nadia`.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "Nadia"
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Todas as publicações da Nadia devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you known...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```
+ Vamos usar aquela com o título `"The cutest dog in the world"`. Anote seu `id` pois ele será usado posteriormente.

Agora vamos tentar adicionar uma tag `dog`.
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation addTag {
    addTag(id:10 tag: "dog") {
      id
      title
      tags
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação é atualizada com a nova tag.

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

Adicione uma ou mais tags da seguinte forma:
+ Atualize a mutação para alterar o argumento `tag` para `puppy`.

  ```
  mutation addTag {
    addTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação é atualizada com a nova tag.

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

Também é possível excluir tags:
+ No painel **Consultas**, cole a mutação a seguir. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

  ```
  mutation removeTag {
    removeTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ A publicação é atualizada e a tag `puppy` é excluída.

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

Também é possível pesquisar todas as publicações com uma tag:
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  query allPostsByTag {
    allPostsByTag(tag: "dog") {
      posts {
        id
        title
        tags
      }
      nextToken
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Todos as publicações com a tag `dog` são retornadas da seguinte forma:

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

## Uso de listas e mapas
<a name="using-lists-and-maps"></a>

Além de usar conjuntos do DynamoDB, também é possível usar listas e mapas do DynamoDB para modelar dados complexos em um único objeto.

Vamos adicionar a capacidade de adicionar comentários em publicações. Isso será modelado como uma lista de objetos de mapa no objeto `Post` no DynamoDB.

 **Observação:** em um aplicativo real, os comentários seriam modelados em suas próprias tabelas. Para este tutorial, basta adicioná-los à tabela `Post`.
+ Escolha a guia **Esquema**.
+ No painel **Esquema**, adicione um novo tipo `Comment` da seguinte forma:

  ```
  type Comment {
      author: String!
      comment: String!
  }
  ```
+ No painel **Esquema**, modifique o tipo `Post` para adicionar um novo campo `comments` da seguinte forma:

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
  }
  ```
+ No painel **Esquema**, modifique o tipo `Mutation` para adicionar uma nova mutação `addComment` da seguinte forma:

  ```
  type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ Escolha **Salvar**.
+ No painel **Tipos de dados** à direita, encontre o campo **addComment** recém-criado no tipo **Mutação** e, em seguida, escolha **Anexar**.
+ Em **Data source name (Nome da fonte de dados)**, escolha **PostDynamoDBTable**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

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

  Essa expressão de atualização anexará uma lista que contém o novo comentário à lista `comments` existente. Se a lista ainda não existir, ela será criada.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte:

  ```
  $utils.toJson($context.result)
  ```
+ Escolha **Salvar**.

### Chamar a API para adicionar um comentário
<a name="call-the-api-to-add-a-comment"></a>

Agora que você configurou os resolvedores, AWS AppSync sabe como traduzir as `addComment` solicitações recebidas em operações do DynamoDB. `UpdateItem`

Vamos testar adicionando um comentário à mesma publicação na qual as tags foram adicionadas.
+ Escolha a guia **Consultas**.
+ No painel **Consultas**, cole a seguinte consulta:

  ```
  mutation addComment {
    addComment(
      id:10
      author: "Steve"
      comment: "Such a cute dog."
    ) {
      id
      comments {
        author
        comment
      }
    }
  }
  ```
+ Escolha **Executar consulta** (o botão de reprodução laranja).
+ Todas as publicações da Nadia devem aparecer no painel de resultados à direita do painel de consulta. A aparência deve ser semelhante à seguinte:

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

Se você executar a solicitação várias vezes, vários comentários serão anexados à lista.

## Conclusão
<a name="conclusion"></a>

Neste tutorial, você criou uma API que nos permite manipular objetos Post no DynamoDB usando o GraphQL. AWS AppSync Para obter mais informações, consulte a [Referência do modelo de mapeamento do resolvedor](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference).

Para limpar, você pode excluir a API AppSync GraphQL do console.

Para excluir a tabela do DynamoDB e a função do IAM que você criou para este tutorial, você pode executar o seguinte para excluir a pilha ou visitar `AWSAppSyncTutorialForAmazonDynamoDB` o console e excluir CloudFormation a pilha:

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

# Usando AWS Lambda resolvedores em AWS AppSync
<a name="tutorial-lambda-resolvers"></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).

Você pode usar AWS Lambda with AWS AppSync para resolver qualquer campo do GraphQL. Por exemplo, uma consulta do GraphQL pode enviar uma chamada para uma instância do Amazon Relational Database Service (Amazon RDS), e uma mutação do GraphQL pode ser gravada em um stream do Amazon Kinesis. Nesta seção, mostraremos como escrever uma função do Lambda que executa lógica de negócios com base na invocação de uma operação de campo do GraphQL.

## Criar uma função do Lambda
<a name="create-a-lam-function"></a>

O exemplo a seguir mostra uma função do Lambda escrita em `Node.js` que realiza operações diferentes em publicações de blog como parte de um aplicativo de publicação de blog.

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

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

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

Essa função do Lambda recupera uma publicação por ID, adiciona uma publicação, recupera uma lista de publicações e busca publicações relacionadas para determinada publicação.

 **Observação:** a função do Lambda usa a instrução `switch` em `event.field` para determinar qual campo está sendo resolvido no momento.

Crie essa função Lambda usando o console AWS de gerenciamento ou uma AWS CloudFormation pilha. Para criar a função a partir de uma CloudFormation pilha, você pode usar o seguinte comando AWS Command Line Interface (AWS CLI):

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

Você também pode lançar a CloudFormation pilha na AWS região Oeste dos EUA (Oregon) em sua AWS conta aqui:

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

## Configurar a fonte de dados para o Lambda
<a name="configure-data-source-for-lamlong"></a>

Depois de criar a função do Lambda, navegue até a API GraphQL no console AWS AppSync e escolha a guia **Fontes de dados**.

Escolha **Criar fonte de dados**, insira um **Nome de fonte de dados** fácil de usar (por exemplo, **Lambda**) e, em seguida, para **Tipo de fonte de dados**, escolha **Função AWS Lambda **. Em **Região**, escolha a mesma região que a de sua função. (Se você criou a função a partir da CloudFormation pilha fornecida, a função provavelmente está em **US-WEST-2**.) Para **ARN da função do Lambda**, escolha o nome do recurso da Amazon (ARN) da sua função do Lambda.

Depois de escolher sua função Lambda, você pode criar uma nova função AWS Identity and Access Management (IAM) (para a qual AWS AppSync atribui as permissões apropriadas) ou escolher uma função existente que tenha a seguinte política embutida:

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

****  

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

------

Você também deve configurar uma relação de confiança com AWS AppSync a função do IAM da seguinte forma:

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

****  

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

------

## Criar um esquema do GraphQL
<a name="creating-a-graphql-schema"></a>

Agora que a fonte de dados está conectada à função do Lambda, crie um esquema do GraphQL.

No editor de esquemas no AWS AppSync console, certifique-se de que seu esquema corresponda ao seguinte esquema:

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

## Configurar resolvedores
<a name="configuring-resolvers"></a>

Agora que você registrou uma fonte de dados do Lambda e um esquema do GraphQL válido, pode conectar seus campos do GraphQL à sua fonte de dados do Lambda usando resolvedores.

Para criar um resolvedor, você precisará de modelos de mapeamento. Para saber mais sobre modelos de mapeamento, consulte [Resolver Mapping Template Overview](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview).

Para obter mais informações sobre modelos de mapeamento do Lambda, consulte [Resolver mapping template reference for Lambda](resolver-mapping-template-reference-lambda.md#aws-appsync-resolver-mapping-template-reference-lambda).

Nesta etapa, você anexa um resolvedor à função do Lambda para os seguintes campos: `getPost(id:ID!): Post`, `allPosts: [Post]`, `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!` e `Post.relatedPosts: [Post]`.

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Anexar resolvedor** para`getPost(id:ID!): Post`.

Depois, no **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**.

Depois, escolha sua fonte de dados do Lambda. Na seção **modelo de mapeamento da solicitação**, escolha **Invocar e encaminhar argumentos**.

Modifique o objeto `payload` para adicionar o nome do campo. O modelo deve ser semelhante ao seguinte:

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

Na seção **modelo de mapeamento da resposta**, escolha **Retornar o resultado Lambda**.

Nesse caso, use o modelo base como está. Ele deve ter a seguinte aparência:

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

Escolha **Salvar**. Você anexou seu primeiro resolvedor com sucesso. Repita essa operação para os campos restantes da seguinte forma:

Para o modelo de mapeamento da solicitação `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!`:

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

Para o modelo de mapeamento da resposta `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!`:

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

Para o modelo de mapeamento da solicitação `allPosts: [Post]`:

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

Para o modelo de mapeamento da resposta `allPosts: [Post]`:

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

Para o modelo de mapeamento da solicitação `Post.relatedPosts: [Post]`:

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

Para o modelo de mapeamento da resposta `Post.relatedPosts: [Post]`:

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

## Teste da sua API GraphQL
<a name="testing-your-graphql-api"></a>

Agora que a função do Lambda está conectada aos resolvedores do GraphQL, você pode executar algumas mutações e consultas usando o console ou um aplicativo cliente.

No lado esquerdo do AWS AppSync console, escolha **Consultas** e cole o código a seguir:

### Mutação addPost
<a name="addpost-mutation"></a>

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

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

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

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

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

## Retornar erros
<a name="returning-errors"></a>

Qualquer resolução de campo determinada pode resultar em um erro. Com AWS AppSync, você pode gerar erros das seguintes fontes:
+ Modelo de mapeamento da solicitação ou resposta
+ Função do Lambda

### A partir do modelo de mapeamento
<a name="from-the-mapping-template"></a>

Para gerar erros intencionais, você pode usar o método auxiliar `$utils.error` do modelo VTL (Velocity Template Language). Ele utiliza como argumento uma `errorMessage`, um `errorType` e um valor `data` opcional. O `data` é útil para retornar dados adicionais de volta ao cliente quando ocorre um erro. O objeto `data` é adicionado ao `errors` na resposta final do GraphQL.

O exemplo a seguir mostra como usá-lo no modelo de mapeamento da resposta `Post.relatedPosts: [Post]`:

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

Isso produz uma resposta do GraphQL semelhante à seguinte:

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

Em que `allPosts[0].relatedPosts` é *nulo*, pois o erro e o `errorMessage`, o `errorType` e o `data` estão presentes no objeto `data.errors[0]`.

### A partir da função do Lambda
<a name="from-the-lam-function"></a>

AWS AppSync também compreende os erros que a função Lambda gera. O modelo de programação do Lambda permite gerar erros *processados*. Se a função Lambda gerar um erro, AWS AppSync não conseguirá resolver o campo atual. Somente a mensagem de erro retornada a partir do Lambda é definida na resposta. No momento, não é possível enviar quaisquer dados adicionais de volta para o cliente ao gerar um erro a partir da função do Lambda.

 **Observação**: se sua função Lambda gerar um erro *não tratado*, AWS AppSync use a mensagem de erro que o Lambda definiu.

A seguinte função do Lambda gera um erro:

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

Isso retorna uma resposta do GraphQL semelhante à seguinte:

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

## Caso de uso avançado: agrupamento em lotes
<a name="advanced-use-case-batching"></a>

Neste exemplo, a função do Lambda tem um campo `relatedPosts` que retorna uma lista de publicações relacionadas para uma publicação. Nas consultas de exemplo, a invocação do campo `allPosts` a partir da função do Lambda retorna cinco publicações. Como também especificamos que queremos resolver `relatedPosts` para cada publicação retornada, a operação do campo `relatedPosts` será invocada cinco vezes.

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

Embora isso possa não parecer substancial neste exemplo específico, essa busca excessiva combinada pode prejudicar rapidamente o aplicativo.

Se você buscasse `relatedPosts` novamente nas `Posts` relacionadas retornadas na mesma consulta, o número de invocações aumentaria drasticamente.

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

Nessa consulta relativamente simples, AWS AppSync invocaria a função Lambda 1 \$1 5 \$1 25 = 31 vezes.

Esse é um desafio bastante comum e normalmente é chamado de problema N\$11 (nesse caso, N = 5) e pode incorrer em maior latência e mais custo para o aplicativo.

Uma forma de resolver esse problema é agrupar solicitações do resolvedor de campo semelhantes em lotes. Nesse exemplo, em vez da função do Lambda resolver uma lista de publicações relacionadas para uma única publicação, ela resolveria uma lista de publicações relacionadas para um determinado lote de publicações.

Para demonstrar isso, vamos alternar o resolvedor `Post.relatedPosts: [Post]` para um resolvedor habilitado para lotes.

No lado direito do AWS AppSync console, escolha o `Post.relatedPosts: [Post]` resolvedor existente. Altere o modelo de mapeamento da solicitação para o seguinte:

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

Somente o campo `operation` foi alterado de `Invoke` para `BatchInvoke`. O campo de carga útil agora se torna uma matriz do que é especificado no modelo. Neste exemplo, a função do Lambda recebe o seguinte como entrada:

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

Quando `BatchInvoke` for especificado no modelo de mapeamento da solicitação, a função do Lambda recebe uma lista de solicitações e retorna uma lista de resultados.

Especificamente, a lista de resultados deve corresponder ao tamanho e à ordem das entradas da carga útil da solicitação para que AWS AppSync possa corresponder aos resultados adequadamente.

Neste exemplo de processamento em lotes, a função do Lambda retorna um lote de resultados da seguinte forma:

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

A seguinte função do Lambda em Node.js demonstra essa funcionalidade de agrupamento em lotes para o campo `Post.relatedPosts` da seguinte forma:

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

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

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

### Retornar erros individuais
<a name="returning-individual-errors"></a>

Os exemplos anteriores mostram que é possível retornar um único erro a partir da função do Lambda ou gerar um erro a partir dos modelos de mapeamento. Para invocações em lote, gerar um erro a partir da função do Lambda sinaliza um lote inteiro como falho. Isso pode ser aceitável em cenários específicos onde ocorreu um erro irrecuperável, como uma conexão com falha a um armazenamento de dados. No entanto, nos casos em que alguns itens no lote são bem-sucedidos e outros falham, é possível retornar ambos os erros e os dados válidos. Como AWS AppSync exige a resposta em lote para listar elementos que correspondam ao tamanho original do lote, você deve definir uma estrutura de dados que possa diferenciar dados válidos de um erro.

Por exemplo, se houver expectativa da função do Lambda retornar um lote de publicações relacionadas, você poderá optar por retornar uma lista de objetos `Response` em que cada objeto tem campos *data*, *errorMessage* e *errorType* opcionais. Se o campo *errorMessage* estiver presente, isso significa que ocorreu um erro.

O código a seguir mostra como você pode atualizar a função do Lambda:

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

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

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

Para este exemplo, o modelo de mapeamento da resposta a seguir analisa cada item da função do Lambda e sinaliza os erros que ocorrerem:

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

Esse exemplo retorna uma resposta do GraphQL semelhante à seguinte:

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

### Configuração do tamanho máximo do lote
<a name="configure-max-batch-size"></a>

Por padrão, ao usar`BatchInvoke`, AWS AppSync envia solicitações para sua função Lambda em lotes de até cinco itens. Você pode configurar o tamanho máximo do lote dos seus resolvedores do Lambda.

Para configurar o tamanho máximo de lotes em um resolvedor, use o seguinte comando no AWS Command Line Interface (AWS CLI):

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

**nota**  
Ao fornecer um modelo de mapeamento de solicitação, você deve usar a operação `BatchInvoke` para usar lotes.

Você também pode usar o seguinte comando para ativar e configurar o lote em resolvedores diretos do Lambda:

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

### Configuração de tamanho máximo de lote com modelos VTL
<a name="configure-max-batch-size-vtl"></a>

Para resolvedores do Lambda que possuem modelos de solicitação de VTL, o tamanho máximo do lote não terá efeito, a menos que o tenham especificado diretamente como uma operação `BatchInvoke` em VTL. Da mesma forma, se você estiver executando uma mutação de nível superior, o processamento em lote não será conduzido para mutações porque a especificação do GraphQL exige que mutações paralelas sejam executadas sequencialmente.

Por exemplo, considere as seguintes mutações:

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

Usando a primeira mutação, podemos criar 10 `Items` conforme mostrado no trecho abaixo:

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

Neste exemplo, os `Items` não serão agrupados em um grupo de 10, mesmo que o tamanho máximo do lote seja definido como 10 no resolvedor do Lambda. Em vez disso, eles serão executados sequencialmente de acordo com a especificação do GraphQL.

Para realizar uma mutação em lote real, você pode seguir o exemplo abaixo usando a segunda mutação:

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

Para obter mais informações sobre como usar lotes com resolvedores diretos do Lambda, consulte [Resolvedores diretos do Lambda](resolver-mapping-template-reference-lambda.md#direct-lambda-resolvers).

# Usando resolvedores OpenSearch do Amazon Service em AWS AppSync
<a name="tutorial-elasticsearch-resolvers"></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 do Amazon OpenSearch Service a partir de domínios que você provisionou em sua própria AWS conta, desde que eles não existam dentro de uma VPC. Assim que os domínios forem provisionados, conecte-se a eles usando uma fonte de dados, no momento em que pode configurar um resolvedor no esquema para realizar operações do GraphQL, como consultas, mutações e assinaturas. Esse tutorial apresentará alguns exemplos comuns.

Para obter mais informações, consulte a [Referência do modelo de mapeamento do Resolver para OpenSearch](resolver-mapping-template-reference-elasticsearch.md#aws-appsync-resolver-mapping-template-reference-elasticsearch).

## Configuração com um clique
<a name="one-click-setup"></a>

Para configurar automaticamente um endpoint do GraphQL com o AWS AppSync Amazon OpenSearch Service configurado, você pode usar este modelo: AWS CloudFormation 

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

Após a conclusão da AWS CloudFormation implantação, você pode pular diretamente para a execução de consultas e mutações do [GraphQL](#tutorial-elasticsearch-resolvers-perform-queries-mutations).

## Criar um novo domínio OpenSearch de serviço
<a name="create-a-new-es-domain"></a>

Para começar com este tutorial, você precisa de um domínio OpenSearch de serviço existente. Caso não tenha um, use o exemplo a seguir. Observe que pode levar até 15 minutos para que um domínio OpenSearch de serviço seja criado antes que você possa prosseguir com a integração com uma fonte de AWS AppSync dados.

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

Você pode lançar a seguinte AWS CloudFormation pilha na região Oeste dos EUA 2 (Oregon) em sua AWS conta:

 [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)

## Configurar fonte de dados para OpenSearch serviço
<a name="configure-data-source-for-es"></a>

Depois que o domínio OpenSearch Service for criado, navegue até a API do AWS AppSync GraphQL e escolha a guia **Fontes de dados**. Selecione **Novo** e insira um nome acessível para a fonte de dados, como "oss". Em seguida, escolha ** OpenSearch o domínio da Amazon** para o **tipo de fonte de dados**, escolha a região apropriada e você deverá ver seu domínio OpenSearch de serviço listado. Depois de selecioná-la, você pode criar uma nova função e AWS AppSync atribuir as permissões apropriadas à função ou escolher uma função existente, que tenha a seguinte política embutida:

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

****  

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

------

Você também precisará estabelecer uma relação de confiança com AWS AppSync essa função:

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

****  

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

------

Além disso, o domínio do OpenSearch serviço tem sua própria **política de acesso**, que você pode modificar por meio do console do Amazon OpenSearch Service. Você precisará adicionar uma política semelhante à seguinte, com as ações e os recursos apropriados para o domínio do OpenSearch Serviço. Observe que o **principal** será a função da fonte de AppSync dados, que, se você permitir que o console a crie, pode ser encontrada no console do IAM.

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

****  

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

------

## Conexão de um resolvedor
<a name="connecting-a-resolver"></a>

Agora que a fonte de dados está conectada ao seu domínio de OpenSearch serviço, você pode conectá-la ao seu esquema do GraphQL com um resolvedor, conforme mostrado no exemplo a seguir:

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

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

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

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

Observe que há um tipo `Post` definido pelo usuário com um campo de `id`. Nos exemplos a seguir, presumimos que há um processo (que pode ser automatizado) para colocar esse tipo em seu domínio de OpenSearch serviço, que seria mapeado para uma raiz de caminho de`/post/_doc`, onde `post` está o índice. A partir desse caminho raiz, você pode executar pesquisas de documento individuais, pesquisas com curingas com `/id/post*` ou pesquisas de vários documentos com um caminho de `/post/_search`. Por exemplo, se você tiver outro tipo chamado `User`, poderá indexar documentos em um novo índice chamado `user` e depois realizar pesquisas com um **caminho** de `/user/_search`. 

No editor de esquemas no AWS AppSync console, modifique o `Posts` esquema anterior para incluir uma consulta: `searchPosts`

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

Salve o esquema. À direita, em `searchPosts`, selecione **Anexar resolvedor**. No **menu Ação**, escolha **Atualizar runtime** e selecione **Resolvedor de unidade (somente VTL)**. Em seguida, escolha sua fonte OpenSearch de dados do Serviço. Na seção **modelo de mapeamento da solicitação**, selecione o menu suspenso em **Postagens de consulta** para obter um modelo base. Modifique o `path` para `/post/_search`. Ele deve ter a seguinte aparência:

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

Isso pressupõe que o esquema anterior tenha documentos que foram indexados no OpenSearch Serviço sob o campo. `post` Se você estruturar os dados de forma diferente, será necessário atualizar de forma adequada.

Na seção do **modelo de mapeamento de resposta**, você precisa especificar o `_source` filtro apropriado se quiser recuperar os resultados dos dados de uma consulta de OpenSearch serviço e traduzir para o GraphQL. Use o modelo a seguir:

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

## Modificação das pesquisas
<a name="modifying-your-searches"></a>

O modelo de mapeamento da solicitação anterior executa uma consulta simples para todos os registros. Digamos que você queira pesquisar um autor específico. Além disso, digamos que queira que esse autor seja um argumento definido na consulta do GraphQL. No editor de esquema do console do AWS AppSync, adicione uma consulta `allPostsByAuthor`:

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

Agora escolha **Anexar resolvedor** e selecione a fonte OpenSearch de dados do Serviço, mas use o exemplo a seguir no **modelo de mapeamento de resposta**:

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

Observe que o `body` é preenchido com uma consulta de termo para o campo `author`, que é enviada a partir do cliente como um argumento. Também é possível ter informações pré-preenchidas, como texto padrão, ou até mesmo usar outros [utilitários](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference).

Se estiver usando esse resolvedor, preencha o **modelo de mapeamento da resposta** com as mesmas informações que o exemplo anterior.

## Adicionando dados ao OpenSearch serviço
<a name="adding-data-to-es"></a>

Talvez você queira adicionar dados ao seu domínio de OpenSearch serviço como resultado de uma mutação do GraphQL. Esse é um poderoso mecanismo para pesquisa e outras finalidades. Como você pode usar assinaturas do GraphQL para [tornar seus dados em tempo real](aws-appsync-real-time-data.md), ele serve como um mecanismo para notificar os clientes sobre atualizações nos dados em seu domínio de serviço. OpenSearch 

Retorne à página **Esquema** no AWS AppSync console e selecione **Anexar resolvedor** para a `addPost()` mutação. Selecione a fonte OpenSearch de dados do Serviço novamente e use o seguinte **modelo de mapeamento de resposta** para o `Posts` esquema:

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

Como antes, este é um exemplo de como os dados podem ser estruturados. Se tiver diferentes nomes de campos ou índices, é necessário atualizar o `path` e o `body` conforme apropriado. Esse exemplo também mostra como usar `$context.arguments` para preencher o modelo a partir dos argumentos da mutação do GraphQL.

Antes de prosseguir, use o seguinte modelo de mapeamento de resposta, que retornará o resultado da operação de mutação ou informações de erro como saída:

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

## Recuperação de um único documento
<a name="retrieving-a-single-document"></a>

Finalmente, se quiser usar a consulta `getPost(id:ID)` no esquema para retornar um documento individual, encontre essa consulta no editor de esquema do console do AWS AppSync e selecione **Attach resolver (Anexar resolvedor)**. Selecione a fonte OpenSearch de dados do Serviço novamente e use o seguinte modelo de mapeamento:

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

Como o `path` acima usa o argumento `id` com um corpo vazio, isso retorna o único documento. No entanto, é necessário usar o seguinte modelo de mapeamento da resposta, pois agora você está retornando um único item, e não uma lista:

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

## Executar consultas e mutações
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations"></a>

Agora você deve ser capaz de realizar operações do GraphQL em seu domínio OpenSearch Service. Navegue até a guia **Consultas** do AWS AppSync console e adicione um novo registro:

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

Você verá o resultado da mutação à direita. Da mesma forma, agora você pode executar uma `searchPosts` consulta em seu domínio OpenSearch de serviço:

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

## Práticas recomendadas
<a name="best-practices"></a>
+ OpenSearch O serviço deve ser para consultar dados, não como seu banco de dados principal. [Talvez você queira usar o OpenSearch Service em conjunto com o Amazon DynamoDB, conforme descrito em Combinação de resolvedores GraphQL.](tutorial-combining-graphql-resolvers.md#aws-appsync-tutorial-combining-graphql-resolvers)
+ Só conceda acesso ao seu domínio permitindo que a função AWS AppSync de serviço acesse o cluster.
+ Você pode começar pequeno no desenvolvimento, com o cluster de menor custo e, em seguida, migrar para um cluster maior com alta disponibilidade (HA) à medida que entrar na produção.

# Uso de resolvedores locais no AWS AppSync
<a name="tutorial-local-resolvers"></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).

O AWS AppSync permite que você use fontes de dados compatíveis (AWS Lambda, Amazon DynamoDB ou Amazon OpenSearch Service) para executar diversas operações. No entanto, em determinados cenários, uma chamada para uma fonte de dados compatível pode não ser necessária.

É aí que o resolvedor local se torna útil. Em vez de chamar uma fonte de dados remota, o resolvedor local apenas **encaminhará** o resultado do modelo de mapeamento da solicitação para o modelo de mapeamento da resposta. A resolução do campo não deixará o AWS AppSync.

Os resolvedores locais são úteis em vários casos de uso. O caso de uso mais popular é a publicação de notificações sem acionar uma chamada da fonte de dados. Para demonstrar esse caso de uso, vamos criar um aplicativo de paginação, onde os usuários podem paginar uns aos outros. Esse exemplo utiliza *Assinaturas*, portanto se não estiver familiarizado com *Assinaturas*, siga o tutorial [Dados em tempo real](aws-appsync-real-time-data.md).

## Criar o aplicativo de paginação
<a name="create-the-paging-application"></a>

No aplicativo de paginação, os clientes podem assinar uma caixa de entrada e enviar páginas para outros clientes. Cada página inclui uma mensagem. Aqui está o esquema:

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

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

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

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

type Query {
    me: String
}
```

Vamos anexar um resolvedor no campo `Mutation.page`. No painel **Esquema**, clique em *Anexar resolvedor* ao lado da definição do campo no painel à direita. Crie uma nova fonte de dados do tipo *Nenhum* e chame-a de *PageDataSource*.

Para o modelo de mapeamento da solicitação, insira:

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

E para o modelo de mapeamento da resposta, selecione o padrão *Encaminhar o resultado*. Salve o resolvedor. O aplicativo agora está pronto, vamos paginar\$1

## Enviar e assinar páginas
<a name="send-and-subscribe-to-pages"></a>

Para que os clientes recebam páginas, primeiro eles devem assinar uma caixa de entrada.

No painel **Consultas** vamos executar a assinatura `inbox`:

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

 *Nadia* receberá páginas sempre que a mutação `Mutation.page` for invocada. Vamos invocar a mutação executando a mutação:

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

Nós acabamos de demonstrar o uso de resolvedores locais, enviando uma Página e recebendo-a sem sair do AWS AppSync.

# Combinando resolvedores GraphQL em AWS AppSync
<a name="tutorial-combining-graphql-resolvers"></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).

Os resolvedores e campos em um esquema do GraphQL têm relacionamentos 1:1 com um grande grau de flexibilidade. Como uma fonte de dados é configurada em um resolvedor independentemente de um esquema, você tem a capacidade de resolver ou manipular os tipos do GraphQL por meio de diferentes fontes de dados, misturando e combinando em um esquema que melhor atender às suas necessidades.

Os cenários de exemplo a seguir demonstram como misturar e combinar fontes de dados no seu esquema. Antes de começar, recomendamos que você esteja familiarizado com a configuração de fontes de dados e resolvedores para AWS Lambda o Amazon DynamoDB OpenSearch e o Amazon Service, conforme descrito nos tutoriais anteriores.

## Esquema de exemplo
<a name="example-schema"></a>

O esquema abaixo tem um tipo de `Post` com três operações `Query` e três operações `Mutation` definidas:

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

Neste exemplo, existem seis resolvedores para anexar. Uma maneira possível seria que todos viessem de uma tabela do Amazon DynamoDB, chamada `Posts`, em que `AllPosts` executa uma verificação e `searchPosts` executa uma consulta, conforme descrito na [Referência do modelo de mapeamento de resolvedor do DynamoDB](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb). No entanto, existem alternativas para atender às suas necessidades de negócios, como fazer com que essas consultas do GraphQL sejam resolvidas a partir do Lambda ou do Service. OpenSearch 

## Alterar os dados por meio de resolvedores
<a name="alter-data-through-resolvers"></a>

Talvez você precise retornar os resultados de um banco de dados como o DynamoDB (ou o Amazon Aurora) aos clientes com alguns dos atributos alterados. Isso pode ser devido à formatação dos tipos de dados, como diferenças de carimbos de data/hora nos clientes, ou para lidar com problemas de compatibilidade com versões anteriores. Para fins ilustrativos no exemplo a seguir, uma função AWS Lambda manipula os votos positivos e negativos para postagens de blog, atribuindo a elas números aleatórios cada vez que o resolvedor do GraphQL é invocado:

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

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

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

Essa é uma função do Lambda perfeitamente válida e pode ser anexada ao campo `AllPosts` no esquema do GraphQL para que qualquer consulta que retorne todos os resultados obtenha números aleatórios para os votos positivos/negativos.

## DynamoDB e serviços OpenSearch
<a name="ddb-and-es"></a>

Para alguns aplicativos, você pode realizar mutações ou consultas de pesquisa simples no DynamoDB e fazer com que um processo em segundo plano transfira documentos para o Service. OpenSearch Em seguida, você pode simplesmente anexar o `searchPosts` Resolver à fonte OpenSearch de dados do Service e retornar os resultados da pesquisa (de dados originados no DynamoDB) usando uma consulta GraphQL. Isso pode ser extremamente poderoso ao adicionar operações de pesquisa avançadas aos aplicativos, como palavra-chave, correspondências de palavras confusas ou até mesmo pesquisas geoespaciais. A transferência de dados do DynamoDB pode ser feita por meio de um processo ETL ou, como alternativa, você pode transmitir do DynamoDB usando o Lambda. Você pode lançar um exemplo completo disso usando a seguinte AWS CloudFormation pilha na região Oeste dos EUA 2 (Oregon) em sua AWS conta:

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

O esquema desse exemplo permitirá que você adicione publicações usando um resolvedor do DynamoDB da seguinte forma:

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

Isso grava dados no DynamoDB, que então transmite dados via Lambda para o OpenSearch Amazon Service, onde você pode pesquisar todas as postagens em campos diferentes. Por exemplo, como os dados estão no Amazon OpenSearch Service, você pode pesquisar os campos do autor ou do conteúdo com texto de formato livre, mesmo com espaços, da seguinte forma:

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

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

Como os dados são gravados diretamente no DynamoDB, você ainda pode realizar operações de pesquisa de item ou lista eficientes na tabela com as consultas `allPosts{...}` e `singlePost{...}`. Essa pilha usa o seguinte código de exemplo para transmissões do DynamoDB:

 **Observação:** esse código é apenas um exemplo.

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

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

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

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

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

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

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

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

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

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

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

Em seguida, você pode usar os streams do DynamoDB para anexá-los a uma tabela do DynamoDB com uma chave primária de, e qualquer alteração na origem do DynamoDB será `id` transmitida para o seu domínio de serviço. OpenSearch Para obter mais informações sobre como configurar isso, consulte a [documentação de Transmissões do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html).

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

# Executando transações do DynamoDB no AWS AppSync
<a name="tutorial-dynamodb-transact"></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 de transação do Amazon DynamoDB em uma ou mais tabelas em uma única região. As operações compatíveis são `TransactGetItems` e `TransactWriteItems`. Ao usar esses recursos no AWS AppSync, você pode realizar 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
+ Grave registros na transação em uma ou mais tabelas de uma all-or-nothing forma
+ Executar transações quando algumas condições são atendidas

## 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 de transação 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: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/*"
            ]
        }
    ]
}
```

------

 **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 de transação 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 chamadas de transação nas tabelas é 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. **TransactTutorial** 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 de transação. Chamaremos a tabela de `empty`.

Teremos duas tabelas chamadas **savingAccounts** e **checkingAccounts**, ambas com `accountNumber` como chave de partição, e uma tabela **transactionHistory** com `transactionId` como chave de partição.

Para esse tutorial, qualquer função com a seguinte política em linha funcionará. Substitua `region` e `accountId` por sua região e seu ID da conta:

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

------

## Transações
<a name="transactions"></a>

Neste exemplo, o contexto é uma transação bancária clássica, onde usaremos `TransactWriteItems` para:
+ Transferir dinheiro de contas poupanças para contas correntes
+ Gerar novos registros para cada transação

E, então, vamos usar `TransactGetItems` para recuperar detalhes de contas poupanças e contas correntes.

**Atenção**  
`TransactWriteItems` não é compatível quando usado com detecção e resolução de conflitos. Essas configurações devem ser desativadas para evitar possíveis erros.

Nós definimos nosso esquema GraphQL da seguinte forma:

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

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

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

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

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

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

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

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

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

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

### TransactWriteItems - Preencher contas
<a name="transactwriteitems-populate-accounts"></a>

Para transferir dinheiro entre contas, precisamos preencher a tabela com os detalhes. Usaremos a operação do GraphQL `Mutation.populateAccounts` para fazer isso.

Na seção Esquema, clique em **Anexar** ao lado da operação `Mutation.populateAccounts`. Vá para VTL Unit Resolvers e escolha a mesma fonte de dados do `TransactTutorial`.

Agora, use o seguinte modelo de mapeamento da solicitação:

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

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

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

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

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

E o seguinte modelo de mapeamento de resposta:

 **Modelo de mapeamento da resposta** 

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

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

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

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

$util.toJson($transactionResult)
```

Salve o resolvedor e navegue até a seção **Consultas** do AWS AppSync console para preencher as contas.

Execute a seguinte mutação:

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

Nós preenchemos três contas poupanças e três contas correntes em uma mutação.

Use o console do DynamoDB para confirmar se os dados aparecem nas tabelas **savingAccounts** e **checkingAccounts**.

### TransactWriteItems - Transferir dinheiro
<a name="transactwriteitems-transfer-money"></a>

Anexe um resolvedor à mutação `transferMoney` com o seguinte **Modelo de mapeamento da solicitação**. Observe se os valores de `amounts`, `savingAccountNumbers` e `checkingAccountNumbers` são os mesmos.

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

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

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

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

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

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

Teremos três transações bancárias em uma única operação `TransactWriteItems`. Use o seguinte **Modelo de mapeamento de resposta**:

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

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

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

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

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

$util.toJson($transactionResult)
```

Agora navegue até a seção **Consultas** do console AWS AppSync e execute a mutação **transferMoney** da seguinte forma:

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

Nós enviamos duas transações bancárias em uma mutação. Use o console do DynamoDB para validar se os dados aparecem nas tabelas **savingAccounts**, **checkingAccounts** e **transactionHistory**.

### TransactGetItems - Recuperar contas
<a name="transactgetitems-retrieve-accounts"></a>

Para recuperar os detalhes das contas poupanças e contas-correntes em uma única solicitação transacional, anexaremos um resolvedor à operação do GraphQL `Query.getAccounts` no nosso esquema. Selecione **Anexar**, vá para VTL Unit Resolvers e, na próxima tela, escolha a mesma fonte de dados do `TransactTutorial` criada no início do tutorial. Configure os modelos da seguinte maneira:

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

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

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

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

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

 **Modelo de mapeamento da resposta** 

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

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

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

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

$util.toJson($transactionResult)
```

Salve o resolvedor e navegue até **as seções Consultas** do AWS AppSync console. Para recuperar as contas poupanças e contas correntes, execute a seguinte consulta:

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

Demonstramos com sucesso o uso das transações do DynamoDB usando. AWS AppSync

# Usando resolvedores HTTP em AWS AppSync
<a name="tutorial-http-resolvers"></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 permite que você use fontes de dados compatíveis (ou seja, Amazon DynamoDB AWS Lambda, Amazon Service ou OpenSearch Amazon Aurora) para realizar várias operações, além de quaisquer endpoints HTTP arbitrários para resolver campos do GraphQL. Depois que os endpoints HTTP estiverem disponíveis, conecte-se a eles usando uma fonte de dados. Em seguida, configure um resolvedor no esquema para executar operações do GraphQL, como consultas, mutações e assinaturas. Esse tutorial apresentará alguns exemplos comuns.

Neste tutorial, você usa uma API REST (criada usando o Amazon API Gateway e o Lambda) com um endpoint GraphQL AWS AppSync .

## Configuração com um clique
<a name="one-click-setup"></a>

Se você quiser configurar automaticamente um endpoint do GraphQL AWS AppSync com um endpoint HTTP configurado (usando o Amazon API Gateway e o Lambda), você pode usar o seguinte modelo: AWS CloudFormation 

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

## Criar uma API REST
<a name="creating-a-rest-api"></a>

Você pode usar o AWS CloudFormation modelo a seguir para configurar um endpoint REST que funcione para este tutorial:

[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)

A AWS CloudFormation pilha executa as seguintes etapas:

1. Configura uma função do Lambda, que contém a lógica de negócios para o seu microsserviço.

1. Configura uma API REST do API Gateway com a seguinte combinação endpoint/method/content de tipos:


****  

| Caminho do recurso da API | Método HTTP | Tipo de conteúdo compatível | 
| --- | --- | --- | 
|  /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  | 

## Criação da API GraphQL
<a name="creating-your-graphql-api"></a>

Para criar a API GraphQL em: AWS AppSync
+ Abra o AWS AppSync console e escolha **Criar API**.
+ Para o nome da API, digite `UserData`.
+ Selecione **Esquema personalizado**.
+ Escolha **Criar**.

O AWS AppSync console cria uma nova API GraphQL para você usando o modo de autenticação por chave de API. É possível usar o console para configurar o restante da API GraphQL e executar consultas nela durante o restante desse tutorial.

## Criar um esquema do GraphQL
<a name="creating-a-graphql-schema"></a>

Agora que você tem uma API GraphQL, vamos criar um esquema do GraphQL. No editor de esquemas no AWS AppSync console, verifique se o esquema corresponde ao esquema a seguir:

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

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

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

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

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

## Configurar a fonte de dados HTTP
<a name="configure-your-http-data-source"></a>

Para configurar a fonte de dados HTTP, faça o seguinte:
+ Na **DataSources**guia, escolha **Novo** e digite um nome amigável para a fonte de dados (por exemplo,`HTTP`).
+ Em **Tipo de fonte de dados**, escolha **HTTP**.
+ Defina o endpoint como o endpoint da API Gateway criado. Lembre-se de não incluir o nome do estágio como parte do endpoint.

 **Observação:** no momento, somente endpoints públicos são compatíveis com o AWS AppSync.

 **Observação:** para obter mais informações sobre as autoridades de certificação que são reconhecidas pelo AWS AppSync serviço, consulte [Autoridades de certificação (CA) reconhecidas por AWS AppSync para endpoints HTTPS](http-cert-authorities.md#aws-appsync-http-certificate-authorities).

## Configurar resolvedores
<a name="configuring-resolvers"></a>

Nesta etapa, conecte a fonte de dados http à consulta **getUser**.

Como configurar o resolvedor :
+ Escolha a guia **Esquema**.
+ No painel **Tipos de dados** à direita no tipo **Consulta**, localize o campo **getUser** e escolha **Associar**.
+ Em **Nome da fonte de dados**, escolha **HTTP**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte código:

```
{
    "version": "2018-05-29",
    "method": "GET",
    "params": {
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "resourcePath": $util.toJson("/v1/users/${ctx.args.id}")
}
```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte código:

```
## return the body
#if($ctx.result.statusCode == 200)
    ##if response is 200
    $ctx.result.body
#else
    ##if response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Escolha a guia **Consulta** e, depois, execute a seguinte consulta:

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

Isso deve retornar a seguinte resposta:

```
{
    "data": {
        "getUser": {
            "id": "1",
            "username": "nadia"
        }
    }
}
```
+ Escolha a guia **Esquema**.
+ No painel **Tipos de dados** à direita em **Mutação**, localize o campo **addUser** e escolha **Anexar**.
+ Em **Nome da fonte de dados**, escolha **HTTP**.
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte código:

```
{
    "version": "2018-05-29",
    "method": "POST",
    "resourcePath": "/v1/users",
    "params":{
      "headers":{
        "Content-Type": "application/json",
      },
      "body": $util.toJson($ctx.args.userInput)
    }
}
```
+ Em **Configurar o modelo de mapeamento da solicitação**, cole o seguinte código:

```
## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
## if the response status code is not 200, then return an error. Else return the body **
#if($ctx.result.statusCode == 200)
    ## If response is 200, return the body.
    $ctx.result.body
#else
    ## If response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Escolha a guia **Consulta** e, depois, execute a seguinte consulta:

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

Isso deve retornar a seguinte resposta:

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

## Serviços de invocação AWS
<a name="invoking-aws-services"></a>

Você pode usar resolvedores HTTP para configurar uma interface AWS de API GraphQL para serviços. As solicitações HTTP AWS devem ser assinadas com o [processo Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) para que seja AWS possível identificar quem as enviou. AWS AppSync calcula a assinatura em seu nome quando você associa uma função do IAM à fonte de dados HTTP.

Você fornece dois componentes adicionais para invocar AWS serviços com resolvedores HTTP:
+ Uma função do IAM com permissões para chamar o AWS serviço APIs
+ Configurar a assinatura na fonte de dados

Por exemplo, se você quiser chamar a [ListGraphqlApis operação](https://docs.aws.amazon.com/appsync/latest/APIReference/API_ListGraphqlApis.html) com resolvedores HTTP, primeiro [crie uma função do IAM](attaching-a-data-source.md#aws-appsync-getting-started-build-a-schema-from-scratch) que AWS AppSync assuma com a seguinte política anexada:

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

****  

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

------

Em seguida, crie a fonte de dados HTTP para AWS AppSync. Neste exemplo, você chamará AWS AppSync na região Oeste dos EUA (Oregon). Configure a seguinte configuração HTTP em um arquivo chamado `http.json`, que inclui a região de assinatura e o nome do serviço:

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

Em seguida, use o AWS CLI para criar a fonte de dados com uma função associada da seguinte forma:

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

Ao anexar um resolvedor ao campo no esquema, use o seguinte modelo de mapeamento de solicitações para chamar AWS AppSync:

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

Quando você executa uma consulta GraphQL para essa fonte de dados, AWS AppSync assina a solicitação usando a função fornecida e inclui a assinatura na solicitação. A consulta retorna uma lista do AWS AppSync GraphQL APIs na sua conta nessa AWS região.

# Usando o Aurora Serverless v2 com AWS AppSync
<a name="tutorial-rds-resolvers"></a>

Conecte sua API GraphQL aos bancos de dados Aurora Serverless usando o. AWS AppSync Essa integração permite a você executar instruções SQL por meio de consultas, mutações e assinaturas do GraphQL, oferecendo uma maneira flexível de interagir com os dados relacionais.

**nota**  
Este tutorial usa a Região `US-EAST-1`.

**Benefícios**
+ Integração perfeita entre GraphQL e bancos de dados relacionais
+ Capacidade de realizar operações SQL por meio de interfaces GraphQL
+ Escalabilidade da tecnologia sem servidor com Aurora Serverless v2
+ Acesso seguro aos dados por meio do AWS Secrets Manager
+ Proteção contra injeção de SQL por meio da limpeza de entradas
+ Recursos de consulta flexíveis, inclusive operações de filtragem e alcance

**Casos de uso comuns**
+ Criação de aplicações escaláveis com requisitos de dados relacionais
+ Para criar APIs isso, é necessário tanto a flexibilidade do GraphQL quanto os recursos do banco de dados SQL
+ Gerenciamento das operações de dados por meio de mutações e consultas do GraphQL
+ Implementação dos padrões de acesso seguro ao banco de dados

Neste tutorial, você aprenderá os itens a seguir.
+ Configurar o cluster do Aurora Serverless v2
+ Habilitar a funcionalidade da API Data
+ Criar e configurar estruturas do banco de dados
+ Definir esquemas do GraphQL para operações do banco de dados
+ Implementar resolvedores para consultas e mutações
+ Proteger o acesso a dados por meio da limpeza de entradas indicada
+ Executar operações de banco de dados variadas por meio de interfaces GraphQL

**Topics**
+ [Configuração do cluster do banco de dados](#create-cluster)
+ [Ativar API de dados](#enable-data-api)
+ [Criar banco de dados e tabela](#create-database-and-table)
+ [Esquema do GraphQL](#graphql-schema)
+ [Conectar a API às operações do banco de dados](#configuring-resolvers)
+ [Modificar os dados por meio da API](#run-mutations)
+ [Recuperar os dados](#run-queries)
+ [Proteger o acesso a dados](#input-sanitization)

## Configuração do cluster do banco de dados
<a name="create-cluster"></a>

**Antes de adicionar uma fonte de dados do Amazon RDS AWS AppSync, você deve primeiro habilitar uma API de dados em um cluster Aurora Serverless v2 e configurar um segredo usando. *AWS Secrets Manager*** Você pode criar um cluster do Aurora Serverless v2 usando Aurora AWS CLI:

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

Isso retornará um ARN para o cluster.

Depois de criar o cluster, você deverá adicionar uma instância do Aurora Serverless v2 usando o comando a seguir.

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

**nota**  
Estes endpoints levam tempo para serem ativados. Você pode consultar o status no console do Amazon RDS na guia **Conectividade e segurança** do cluster. Você também pode verificar o status do seu cluster com o AWS CLI comando a seguir.   

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

Você pode criar um *segredo* usando o AWS Secrets Manager console ou AWS CLI com um arquivo de entrada, como o seguinte, usando o `USERNAME` e `COMPLEX_PASSWORD` da etapa anterior.

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

Passe isso como um parâmetro para AWS CLI:

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

Isso retornará um ARN para o segredo.

 **Anote o ARN do seu** cluster Aurora Serverless e Secret para uso posterior no AppSync console ao criar uma fonte de dados.

## Ativar API de dados
<a name="enable-data-api"></a>

Você pode ativar a API de dados em seu cluster [seguindo as instruções na documentação do RDS](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html). A API de dados deve ser ativada antes de ser adicionada como fonte de AppSync dados.

## Criar banco de dados e tabela
<a name="create-database-and-table"></a>

Depois de ativar sua API de dados, você pode garantir que ela funcione com o comando `aws rds-data execute-statement` na AWS CLI. Isso garantirá que seu cluster Aurora Serverless seja configurado corretamente antes de adicioná-lo à sua API. AppSync Primeiro crie um banco de dados chamado *TESTDB* com o parâmetro `--sql` da seguinte forma:

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

Se isso for executado sem erro, inclua uma tabela com o comando *create table*:

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

Se tudo tiver sido executado sem problemas, você poderá adicionar o cluster como fonte de dados na sua AppSync API.

## Esquema do GraphQL
<a name="graphql-schema"></a>

Agora que sua API de dados do Aurora Serverless está funcionando com uma tabela, criaremos um esquema do GraphQL e anexaremos resolvedores para executar mutações e assinaturas. Crie uma nova API no AWS AppSync console, navegue até a página **Esquema** e insira o seguinte:

```
type Mutation {
    createPet(input: CreatePetInput!): Pet
    updatePet(input: UpdatePetInput!): Pet
    deletePet(input: DeletePetInput!): Pet
}

input CreatePetInput {
    type: PetType
    price: Float!
}

input UpdatePetInput {
id: ID!
    type: PetType
    price: Float!
}

input DeletePetInput {
    id: ID!
}

type Pet {
    id: ID!
    type: PetType
    price: Float
}

enum PetType {
    dog
    cat
    fish
    bird
    gecko
}

type Query {
    getPet(id: ID!): Pet
    listPets: [Pet]
    listPetsByPriceRange(min: Float, max: Float): [Pet]
}

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

 **Salve** seu esquema e navegue até a página **Fontes de dados** e crie uma nova fonte de dados. Selecione **Banco de dados relacional** para o tipo de fonte de dados e forneça um nome amigável. Use o nome do banco de dados que você criou na última etapa, bem como o **ARN do cluster** em que você o criou. Para a **função**, você pode AppSync criar uma nova função ou criar uma com uma política semelhante à abaixo:

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

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-data:BatchExecuteStatement",
                "rds-data:BeginTransaction",
                "rds-data:CommitTransaction",
                "rds-data:ExecuteStatement",
                "rds-data:RollbackTransaction"
            ],
            "Resource": [
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": [
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
            ]
        }
    ]
}
```

------

Observe que há duas **instruções** nesta política que você está concedendo acesso à função. O primeiro **recurso** é seu cluster Aurora Serverless e o segundo é seu ARN. AWS Secrets Manager Você precisará fornecer **AMBOS** ARNs na configuração da fonte de AppSync dados antes de clicar em **Criar**.

Passe isso como um parâmetro para AWS CLI o.

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

Isso retornará um ARN para o segredo. Anote o ARN do seu cluster e segredo sem servidor do Aurora para mais tarde ao criar uma fonte de dados no console. AWS AppSync 

### Criar a estrutura do banco de dados
<a name="create-database-and-table"></a>

Depois de ativar sua API de dados, você pode garantir que ela funcione com o comando `aws rds-data execute-statement` na AWS CLI. Isso garantirá que seu cluster Aurora Serverless v2 seja configurado corretamente antes de adicioná-lo à sua API. AWS AppSync Primeiro, crie um banco de dados chamado *TESTDB* com o parâmetro `--sql` da maneira a seguir.

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

Se isso for executado sem erros, adicione uma tabela com o comando *create table*.

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

### Projetar a interface da API
<a name="graphql-schema"></a>

Depois que a API Data do Aurora Serverless v2 estiver funcionando com uma tabela, criaremos um esquema do GraphQL e anexaremos resolvedores para realizar mutações e assinaturas. Crie uma nova API no AWS AppSync console, navegue até a página **Esquema** no console e insira o seguinte.

```
type Mutation {
        createPet(input: CreatePetInput!): Pet
        updatePet(input: UpdatePetInput!): Pet
        deletePet(input: DeletePetInput!): Pet
    }
    
    input CreatePetInput {
        type: PetType
        price: Float!
    }
    
    input UpdatePetInput {
        id: ID!
        type: PetType
        price: Float!
    }
    
    input DeletePetInput {
        id: ID!
    }
    
    type Pet {
        id: ID!
        type: PetType
        price: Float
    }
    
    enum PetType {
        dog
        cat
        fish
        bird
        gecko
    }
    
    type Query {
        getPet(id: ID!): Pet
        listPets: [Pet]
        listPetsByPriceRange(min: Float, max: Float): [Pet]
    }
    
    schema {
        query: Query
        mutation: Mutation
    }
```

 **Salve** seu esquema e navegue até a página **Fontes de dados** e crie uma nova fonte de dados. Escolha **Banco de dados relacional** para o tipo **Fonte de dados** e dê um nome amigável. Use o nome do banco de dados que você criou na última etapa, bem como o **ARN do cluster** em que você o criou. Para a **função**, você pode AWS AppSync criar uma nova função ou criar uma com uma política semelhante à seguinte.

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

****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "rds-data:BatchExecuteStatement",
                    "rds-data:BeginTransaction",
                    "rds-data:CommitTransaction",
                    "rds-data:ExecuteStatement",
                    "rds-data:RollbackTransaction"
                ],
                "Resource": [
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
                ]
            },
            {
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue"
                ],
                "Resource": [
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
                ]
            }
        ]
    }
```

------

Observe que há duas **instruções** nesta política que você está concedendo acesso à função. O primeiro **recurso** é seu cluster Aurora Serverless v2 e o segundo é seu ARN. AWS Secrets Manager Você precisará fornecer **AMBOS** ARNs na configuração da fonte de AWS AppSync dados antes de clicar em **Criar**.

## Conectar a API às operações do banco de dados
<a name="configuring-resolvers"></a>

Agora que temos um esquema do GraphQL válido e uma fonte de dados RDS, você pode anexar resolvedores aos campos do GraphQL ao esquema. Nossa API oferecerá os seguintes recursos:

1. criar um animal de estimação usando o campo *Mutation.createPet*

1. atualizar um animal de estimação usando o campo *Mutation.updatePet*

1. excluir um animal de estimação usando o campo *Mutation.deletePet*

1. obter um único animal de estimação usando o campo *Query.getPet*

1. listar tudo usando o campo *Query.listPets*

1. liste animais de estimação em uma faixa de preço usando o *Query. listPetsByPriceRange*campo

### Mutation.createPet
<a name="mutation-createpet"></a>

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`createPet(input: CreatePetInput!): Pet`. Escolha a fonte de dados do RDS. Na seção **modelo de mapeamento de solicitação**, adicione o seguinte modelo:

```
#set($id=$utils.autoId())
{
"version": "2018-05-29",
    "statements": [
        "insert into Pets VALUES (:ID, :TYPE, :PRICE)",
        "select * from Pets WHERE id = :ID"
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

O sistema executa instruções SQL sequencialmente, com base na ordem na matriz de **instruções**. Os resultados retornarão na mesma ordem. Como essa é uma mutação, executaremos uma instrução *select* depois de *insert* para recuperar os valores confirmados a fim de preencher o modelo de mapeamento de respostas do GraphQL.

Na seção **modelo de mapeamento de resposta**, adicione o seguinte modelo:

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

Como as *instruções* têm duas consultas SQL, precisamos especificar o segundo resultado na matriz que retorna do banco de dados com: `$utils.rds.toJsonString($ctx.result))[1][0])`.

### Mutation.updatePet
<a name="mutation-updatepet"></a>

No editor de esquemas no AWS AppSync console, escolha **Anexar resolvedor** para`updatePet(input: UpdatePetInput!): Pet`. Escolha a **fonte de dados do RDS**. Na seção **Modelo de mapeamento de solicitações**, adicione o modelo a seguir.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"),
        $util.toJson("select * from Pets WHERE id = :ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

Na seção **Modelo de mapeamento de respostas**, adicione o modelo a seguir.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

### Mutation.deletePet
<a name="mutation-deletepet"></a>

No editor de esquemas no AWS AppSync console, escolha **Anexar resolvedor** para`deletePet(input: DeletePetInput!): Pet`. Escolha a **fonte de dados do RDS**. Na seção **Modelo de mapeamento de solicitações**, adicione o modelo a seguir.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("select * from Pets WHERE id=:ID"),
        $util.toJson("delete from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id"
    }
}
```

Na seção **Modelo de mapeamento de respostas**, adicione o modelo a seguir.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.getPet
<a name="query-getpet"></a>

Agora que as mutações estão criadas para o esquema, conectamos as três consultas para demonstrar como obter itens individuais, listas e aplicar a filtragem SQL. No **editor de esquemas** no AWS AppSync console, escolha **Anexar resolvedor** para`getPet(id: ID!): Pet`. Escolha a **fonte de dados do RDS**. Na seção **Modelo de mapeamento de solicitações**, adicione o modelo a seguir.

```
{
"version": "2018-05-29",
        "statements": [
            $util.toJson("select * from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.id"
    }
}
```

Na seção **modelo de mapeamento de resposta**, adicione o seguinte modelo:

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.listPets
<a name="query-listpets"></a>

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`getPet(id: ID!): Pet`. Escolha a **fonte de dados do RDS**. Na seção **Modelo de mapeamento de solicitações**, adicione o modelo a seguir.

```
{
    "version": "2018-05-29",
    "statements": [
        "select * from Pets"
    ]
}
```

Na seção **Modelo de mapeamento de respostas**, adicione o modelo a seguir.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

### Consulta. listPetsByPriceRange
<a name="query-listpetsbypricerange"></a>

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`getPet(id: ID!): Pet`. Escolha a **fonte de dados do RDS**. Na seção **Modelo de mapeamento de solicitações**, adicione o modelo a seguir.

```
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.max),
        ":MIN": $util.toJson($ctx.args.min)
    }
}
```

Na seção **modelo de mapeamento de resposta**, adicione o seguinte modelo:

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

## Modificar os dados por meio da API
<a name="run-mutations"></a>

Agora que você configurou todos os seus resolvedores com instruções SQL e conectou a API do GraphQL à API de dados do Aurora Serverless, é possível começar a realizar mutações e consultas. No AWS AppSync console, escolha a guia **Consultas** e digite o seguinte para criar um animal de estimação:

```
mutation add {
    createPet(input : { type:fish, price:10.0 }){
        id
        type
        price
    }
}
```

A resposta deve conter o *id*, o *tipo* e o *preço* da seguinte forma:

```
{
  "data": {
    "createPet": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "type": "fish",
      "price": "10.0"
    }
  }
}
```

Você pode modificar este item executando a mutação *updatePet*:

```
mutation update {
    updatePet(input : {
        id: ID_PLACEHOLDER,
        type:bird,
        price:50.0
    }){
        id
        type
        price
    }
}
```

Observe que usamos o *id* que foi retornado da operação *createPet* anteriormente. Esse será um valor único para o seu registro, já que o resolvedor utilizou `$util.autoId()`. Você pode excluir um registro de maneira semelhante:

```
mutation delete {
    deletePet(input : {id:ID_PLACEHOLDER}){
        id
        type
        price
    }
}
```

Crie alguns registros com a primeira mutação com valores diferentes para *preço* e execute algumas consultas.

## Recuperar os dados
<a name="run-queries"></a>

Ainda na guia **Consultas** do console, use a instrução a seguir para listar todos os registros criados por você.

```
query allpets {
    listPets {
        id
        type
        price
    }
}
```

Aproveite o predicado SQL *WHERE* que estava `where price > :MIN and price < :MAX` em nosso modelo de mapeamento para o *Query. listPetsByPriceRange*com a seguinte consulta do GraphQL:

```
query petsByPriceRange {
    listPetsByPriceRange(min:1, max:11) {
        id
        type
        price
    }
}
```

Você só deve ver registros com um *preço* acima de US\$1 1 ou abaixo de US\$1 10. Por fim, é possível realizar consultas para recuperar registros individuais da seguinte maneira:

```
query onePet {
    getPet(id:ID_PLACEHOLDER){
        id
        type
        price
    }
}
```

## Proteger o acesso a dados
<a name="input-sanitization"></a>

A injeção de SQL é uma vulnerabilidade de segurança em aplicações de banco de dados. Isso ocorre quando invasores inserem um código SQL mal-intencionado por meio dos campos de entrada do usuário. Isso pode permitir acesso não autorizado aos dados do banco de dados. É recomendável validar e limpar com cuidado todas as entradas do usuário antes do processamento usando `variableMap` para proteção contra ataques de injeção de SQL. Se mapas de variáveis não forem usados, você será responsável por limpar os argumentos das operações do GraphQL. Uma maneira de fazer isso é fornecer etapas de validação específicas de entrada no modelo de mapeamento de solicitação antes da execução de uma instrução SQL em relação à API de dados. Vamos ver como podemos modificar o modelo de mapeamento de solicitação do exemplo `listPetsByPriceRange`. Em vez de contar apenas com a entrada do usuário, você pode fazer o seguinte:

```
#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice))

#set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice))


#if (!$validMaxPrice || !$validMinPrice)
    $util.error("Provided price input is not valid.")
#end
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.maxPrice),
        ":MIN": $util.toJson($ctx.args.minPrice)
    }
}
```

Outra maneira de proteger contra entradas invasivas ao executar resolvedores em relação à API de dados é usar instruções preparadas junto com o procedimento armazenado e entradas parametrizadas. Por exemplo, no resolvedor para `listPets`, defina o seguinte procedimento que executa *select* como uma instrução preparada:

```
CREATE PROCEDURE listPets (IN type_param VARCHAR(200))
  BEGIN
     PREPARE stmt FROM 'SELECT * FROM Pets where type=?';
     SET @type = type_param;
     EXECUTE stmt USING @type;
     DEALLOCATE PREPARE stmt;
  END
```

Crie isso na instância do Aurora Serverless v2.

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \
--schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx"  \
--region us-east-1  --database "DB_NAME" \
--sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"
```

O código de resolvedor resultante para listPets foi simplificado, pois agora simplesmente chamamos o procedimento armazenado. No mínimo, qualquer entrada de string deve ter aspas simples [com caracteres de escape](#escaped).

```
#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type))
#if (!$validType)
    $util.error("Input for 'type' is not valid.", "ValidationError")
#end

{
    "version": "2018-05-29",
    "statements": [
        "CALL listPets(:type)"
    ]
    "variableMap": {
        ":type": $util.toJson($ctx.args.type.replace("'", "''"))
    }
}
```

### Uso das strings de escape
<a name="escaped"></a>

Use aspas simples para marcar o início e o fim dos literais de string em uma instrução SQL; por exemplo, `'some string value'`. Para permitir que valores de string com um ou mais caracteres de aspas simples (`'`) sejam usados em uma string, cada um deve ser substituído por duas aspas simples (`''`). Por exemplo, se a string de entrada for `Nadia's dog`, você deverá inserir caracteres de escape nela para a instrução SQL, como

```
update Pets set type='Nadia''s dog' WHERE id='1'
```

# Usando resolvedores de pipeline em AWS AppSync
<a name="tutorial-pipeline-resolvers"></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 fornece uma maneira simples de conectar um campo GraphQL a uma única fonte de dados por meio de resolvedores de unidades. No entanto, a execução de uma única operação pode não ser suficiente. Os resolvedores de pipeline oferecem a capacidade de executar operações em série mediante fontes de dados. Crie funções na sua API e anexe-as a um resolvedor de pipeline. Cada resultado de execução da função é direcionado para a próxima até que nenhuma função fique sem execução. Com os resolvedores de pipeline, agora você pode criar fluxos de trabalho mais complexos diretamente no AWS AppSync. Neste tutorial, você cria um aplicativo de visualização de fotos simples, no qual os usuários podem publicar e visualizar fotos publicadas por seus amigos.

## Configuração com um clique
<a name="one-click-setup"></a>

Se você quiser configurar automaticamente o endpoint do GraphQL AWS AppSync com todos os resolvedores configurados e os AWS recursos necessários, você pode usar o seguinte modelo: AWS CloudFormation 

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

Esta pilha cria os seguintes recursos na sua conta:
+ Função do IAM AWS AppSync para acessar os recursos em sua conta
+ 2 Tabelas do DynamoDB
+ 1 grupo de usuários do Amazon Cognito
+ 2 grupos de usuários do Amazon Cognito
+ 3 usuários do grupo de usuários do Amazon Cognito
+ 1 AWS AppSync API

No final do processo de criação da AWS CloudFormation pilha, você recebe um e-mail para cada um dos três usuários do Amazon Cognito que foram criados. Cada e-mail contém uma senha temporária que você usa para fazer login como usuário do Amazon Cognito no AWS AppSync console. Salve as senhas para o restante do tutorial.

## Configuração manual
<a name="manual-setup"></a>

Se você preferir passar manualmente por um step-by-step processo pelo AWS AppSync console, siga o processo de configuração abaixo.

### Configurando seus AWS AppSync recursos não relacionados
<a name="setting-up-your-non-aws-appsync-resources"></a>

A API se comunica com duas tabelas do DynamoDB: uma tabela **pictures** que armazena fotos e uma tabela **friends** que armazena os relacionamentos entre os usuários. A API é configurada para usar o grupo de usuários do Amazon Cognito como tipo de autenticação. A CloudFormation pilha a seguir configura esses recursos na conta.

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

No final do processo de criação da AWS CloudFormation pilha, você recebe um e-mail para cada um dos três usuários do Amazon Cognito que foram criados. Cada e-mail contém uma senha temporária que você usa para fazer login como um usuário do Amazon Cognito no console do AWS AppSync. Salve as senhas para o restante do tutorial.

### Criação da API GraphQL
<a name="creating-your-graphql-api"></a>

Para criar a API GraphQL em: AWS AppSync

1. Abra o AWS AppSync console, escolha **Build From Scratch** e escolha **Start**.

1. Defina o nome da API como `AppSyncTutorial-PicturesViewer`.

1. Escolha **Criar**.

O AWS AppSync console cria uma nova API GraphQL para você usando o modo de autenticação por chave de API. Você pode usar o console para configurar o restante da API GraphQL e executar consultas nela durante o restante desse tutorial.

### Configurar a API do GraphQL
<a name="configuring-the-graphql-api"></a>

Você precisa configurar a AWS AppSync API com o grupo de usuários do Amazon Cognito que você acabou de criar.

1. Escolha a guia **Configurações**.

1. Na seção **Authorization Type**, escolha *Grupo de usuários do Amazon Cognito*.

1. Em **Configuração do grupo de usuários**, escolha **US-WEST-2** para a *região da AWS *.

1. Escolha o **AppSyncTutorial-** grupo de UserPool usuários.

1. Escolha **DENY** como *Ação padrão*.

1. Deixe o campo **regex AppId do cliente** em branco.

1. Escolha **Salvar**.

Agora, a API está configurada para usar o grupo de usuários do Amazon Cognito como seu tipo de autorização.

### Configuração da fonte de dados para as tabelas do DynamoDB
<a name="configuring-data-sources-for-the-ddb-tables"></a>

**Depois que as tabelas do DynamoDB forem criadas, navegue até a API do AWS AppSync GraphQL no console e escolha a guia Fontes de dados.** Agora, você criará uma fonte de dados AWS AppSync para cada uma das tabelas do DynamoDB que você acabou de criar.

1. Escolha a guia **Fonte de dados**.

1. Selecione **Novo** para criar uma nova fonte de dados.

1. Para o nome da fonte de dados, insira `PicturesDynamoDBTable`.

1. Para o tipo de fonte de dados, escolha **Tabela do Amazon DynamoDB**.

1. Para a região, escolha **US-WEST-2**.

1. Na lista de tabelas, escolha a tabela **AppSyncTutorial-Pictures DynamoDB**.

1. Na seção **Criar ou usar um perfil existente**, escolha **Perfil existente**.

1. Escolha a função que acabou de ser criada a partir do CloudFormation modelo. Se você não alterou o *ResourceNamePrefix*, o nome da função deverá ser **AppSyncTutorialDBRole-Dynamo**.

1. Escolha **Criar**.

Repita o mesmo processo para a tabela de **amigos**. O nome da tabela do DynamoDB deve **AppSyncTutorialser** -Friends se você não alterou *ResourceNamePrefix*o parâmetro no momento da criação da pilha. CloudFormation 

### Criação do esquema do GraphQL
<a name="creating-the-graphql-schema"></a>

Agora que as fontes de dados estão conectadas às suas tabelas do DynamoDB, vamos criar um esquema do GraphQL. No editor de esquemas no AWS AppSync console, verifique se o esquema corresponde ao esquema a seguir:

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

type Mutation {
    createPicture(input: CreatePictureInput!): Picture!
    @aws_auth(cognito_groups: ["Admins"])
    createFriendship(id: ID!, target: ID!): Boolean
    @aws_auth(cognito_groups: ["Admins"])
}

type Query {
    getPicturesByOwner(id: ID!): [Picture]
    @aws_auth(cognito_groups: ["Admins", "Viewers"])
}

type Picture {
    id: ID!
    owner: ID!
    src: String
}

input CreatePictureInput {
    owner: ID!
    src: String!
}
```

Escolha **Salvar esquema** para salvar o esquema.

Alguns dos campos do esquema foram anotados com a diretiva *@aws\$1auth*. Como a configuração de ação padrão da API é definida como *DENY*, a API rejeita todos os usuários que não são membros dos grupos mencionados na diretiva *@aws\$1auth*. Para obter mais informações sobre como proteger sua API, você pode ler a página [Segurança](security-authz.md#aws-appsync-security). **Nesse caso, somente usuários administradores têm acesso aos campos *mutation.createPicture e *mutation.createFriendship**, enquanto usuários que são membros dos grupos Administradores ou Visualizadores podem acessar a Consulta.** * getPicturesByCampo do proprietário*. Todos os outros usuários não têm acesso.

### Configurar resolvedores
<a name="configuring-resolvers"></a>

Agora que você tem um esquema do GraphQL válido e duas fontes de dados, é possível anexar resolvedores aos campos do GraphQL no esquema. A API oferece os seguintes recursos:
+ Crie uma foto por meio do campo *Mutation.createPicture*
+ Estabeleça a amizade por meio do campo *Mutation.createFriendship*
+ Recupere uma foto por meio do campo *Query.getPicture*

#### Mutation.createPicture
<a name="mutation-createpicture"></a>

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`createPicture(input: CreatePictureInput!): Picture!`. Escolha a fonte de dados do *PicturesDynamoDBTable*DynamoDB. Na seção **modelo de mapeamento de solicitação**, adicione o seguinte modelo:

```
#set($id = $util.autoId())

{
    "version" : "2018-05-29",

    "operation" : "PutItem",

    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($id),
        "owner": $util.dynamodb.toDynamoDBJson($ctx.args.input.owner)
    },

    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}
```

Na seção **modelo de mapeamento de resposta**, adicione o seguinte modelo:

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($ctx.result)
```

A funcionalidade de criação de fotos está concluída. Você está salvando uma foto na tabela **Pictures**, usando um UUID gerado aleatoriamente como id da foto e usando o nome de usuário do Cognito como proprietário da foto.

#### Mutation.createFriendship
<a name="mutation-createfriendship"></a>

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`createFriendship(id: ID!, target: ID!): Boolean`. Escolha a fonte de dados do **FriendsDynamoDBTable**DynamoDB. Na seção **modelo de mapeamento de solicitação**, adicione o seguinte modelo:

```
#set($userToFriendFriendship = { "userId" : "$ctx.args.id", "friendId": "$ctx.args.target" })
#set($friendToUserFriendship = { "userId" : "$ctx.args.target", "friendId": "$ctx.args.id" })
#set($friendsItems = [$util.dynamodb.toMapValues($userToFriendFriendship), $util.dynamodb.toMapValues($friendToUserFriendship)])

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        ## Replace 'AppSyncTutorial-' default below with the ResourceNamePrefix you provided in the CloudFormation template
        "AppSyncTutorial-Friends": $util.toJson($friendsItems)
    }
}
```

Importante: no modelo de **BatchPutItem**solicitação, o nome exato da tabela do DynamoDB deve estar presente. O nome padrão da tabela é *AppSyncTutorial-Friends.* Se você estiver usando o nome errado da tabela, você receberá um erro ao AppSync tentar assumir a função fornecida.

Para simplificar este tutorial, proceda como se a solicitação de amizade tivesse sido aprovada e salve a entrada do relacionamento diretamente na **AppSyncTutorialFriends**tabela.

Efetivamente, você está armazenando dois itens para cada amizade, pois o relacionamento é bidirecional. Para obter mais detalhes sobre as melhores práticas do Amazon DynamoDB para many-to-many representar relacionamentos, consulte [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html) Best Practices.

Na seção **modelo de mapeamento de resposta**, adicione o seguinte modelo:

```
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
true
```

Nota: certifique-se de que o seu modelo de solicitação contenha o nome da tabela correta. O nome padrão é *AppSyncTutorial-Friends*, mas o nome da tabela pode ser diferente se você alterar o CloudFormation **ResourceNamePrefix**parâmetro.

#### Consulta. getPicturesByProprietário
<a name="query-getpicturesbyowner"></a>

Agora que você tem amizades e fotos, precisa fornecer aos usuários a capacidade de ver as fotos de seus amigos. Para atender a esse requisito, você precisa primeiro verificar se o solicitante é amigo do proprietário e, por fim, consultar as fotos.

Como essa funcionalidade requer duas operações de fonte de dados, você criará duas funções. A primeira função, **isFriend**, verifica se o solicitante e o proprietário são amigos. A segunda função, **getPicturesByProprietário**, recupera as fotos solicitadas com o ID do proprietário. Vamos dar uma olhada no fluxo de execução abaixo para o resolvedor proposto na *Consulta. getPicturesByCampo do proprietário*:

1. Antes do modelo de mapeamento: prepare os argumentos de entrada de contexto e campo.

1. Função isFriend: verifica se o solicitante é o proprietário da foto. Caso contrário, ele verifica se os usuários solicitantes e proprietários são amigos fazendo uma operação do GetItem DynamoDB na tabela de amigos.

1. getPicturesByFunção de proprietário: recupera imagens da tabela Imagens usando uma operação de consulta do DynamoDB no Índice Secundário Global do índice *do* proprietário.

1. Após o modelo de mapeamento: mapeie o resultado da foto para que os atributos do DynamoDB mapeiem corretamente para os campos esperados do tipo GraphQL.

Primeiro, vamos criar as funções.

##### Função isFriend
<a name="isfriend-function"></a>

1. Escolha a guia **Funções**.

1. Escolha **Criar função** para criar uma função.

1. Para o nome da fonte de dados, insira `FriendsDynamoDBTable`.

1. Para o nome da função, digite *isFriend*.

1. Dentro da área de texto do modelo de mapeamento de solicitação, cole o seguinte modelo:

   ```
   #set($ownerId = $ctx.prev.result.owner)
   #set($callerId = $ctx.prev.result.callerId)
   
   ## if the owner is the caller, no need to make the check
   #if($ownerId == $callerId)
       #return($ctx.prev.result)
   #end
   
   {
       "version" : "2018-05-29",
   
       "operation" : "GetItem",
   
       "key" : {
           "userId" : $util.dynamodb.toDynamoDBJson($callerId),
           "friendId" : $util.dynamodb.toDynamoDBJson($ownerId)
       }
   }
   ```

1. Dentro da área de texto do modelo de mapeamento de resposta, cole o seguinte modelo:

   ```
   #if($ctx.error)
       $util.error("Unable to retrieve friend mapping message: ${ctx.error.message}", $ctx.error.type)
   #end
   
   ## if the users aren't friends
   #if(!$ctx.result)
       $util.unauthorized()
   #end
   
   $util.toJson($ctx.prev.result)
   ```

1. Escolha **Criar função**.

Resultado: você criou a função **isFriend**.

##### getPicturesByFunção do proprietário
<a name="getpicturesbyowner-function"></a>

1. Escolha a guia **Funções**.

1. Escolha **Criar função** para criar uma função.

1. Para o nome da fonte de dados, insira `PicturesDynamoDBTable`.

1. Para o nome da função, digite `getPicturesByOwner`.

1. Dentro da área de texto do modelo de mapeamento de solicitação, cole o seguinte modelo:

   ```
   {
       "version" : "2018-05-29",
   
       "operation" : "Query",
   
       "query" : {
           "expression": "#owner = :owner",
           "expressionNames": {
               "#owner" : "owner"
           },
           "expressionValues" : {
               ":owner" : $util.dynamodb.toDynamoDBJson($ctx.prev.result.owner)
           }
       },
   
       "index": "owner-index"
   }
   ```

1. Dentro da área de texto do modelo de mapeamento de resposta, cole o seguinte modelo:

   ```
   #if($ctx.error)
       $util.error($ctx.error.message, $ctx.error.type)
   #end
   
   $util.toJson($ctx.result)
   ```

1. Escolha **Criar função**.

Resultado: você criou a função **getPicturesByProprietário**. Agora que as funções foram criadas, anexe um resolvedor de pipeline à *consulta. getPicturesByCampo do proprietário*.

No editor de esquemas no AWS AppSync console, no lado direito, escolha **Attach Resolver** for`Query.getPicturesByOwner(id: ID!): [Picture]`. Na página seguinte, escolha o link **Converter para resolvedor de pipeline** exibido abaixo da lista suspensa da fonte de dados. Use o seguinte para o modelo de mapeamento anterior:

```
#set($result = { "owner": $ctx.args.id, "callerId": $ctx.identity.username })
$util.toJson($result)
```

Na seção **modelo de mapeamento posterior**, use o seguinte modelo:

```
#foreach($picture in $ctx.result.items)
    ## prepend "src://" to picture.src property
    #set($picture['src'] = "src://${picture['src']}")
#end
$util.toJson($ctx.result.items)
```

Escolha **Criar resolvedor**. Você anexou seu primeiro resolvedor de pipeline com sucesso. Na mesma página, adicione as duas funções criadas anteriormente. Na seção de funções, escolha **Adicionar uma função** e escolha ou digite o nome da primeira função, **isFriend**. Adicione a segunda função seguindo o mesmo processo para a função **getPicturesByProprietário**. Certifique-se de que a função **isFriend** apareça primeiro na lista, seguida pela função **getPicturesByOwner**. Você pode usar as setas para cima e para baixo para reorganizar a ordem de execução das funções no pipeline.

Agora que o resolvedor de pipeline foi criado e você anexou as funções, vamos testar a API do GraphQL recém-criada.

## Teste da API GraphQL
<a name="testing-your-graphql-api"></a>

Primeiro, você precisa preencher fotos e amizades executando algumas mutações usando o usuário administrador que você criou. No lado esquerdo do AWS AppSync console, escolha a guia **Consultas.**

### Mutação createPicture
<a name="createpicture-mutation"></a>

1. No AWS AppSync console, escolha a guia **Consultas.**

1. Escolha **Login com grupos de usuários**.

1. No modal, insira o Cognito Sample Client ID que foi criado pela pilha (por exemplo, CloudFormation 37solo6mmhh7k4v63cqdfgdg5d).

1. Insira o nome de usuário que você passou como parâmetro para a CloudFormation pilha. O padrão é **nadia**.

1. Use a senha temporária que foi enviada para o e-mail que você forneceu como parâmetro para a CloudFormation pilha (por exemplo, *UserPoolUserEmail*).

1. Escolha Fazer login. Agora você deve ver o botão renomeado para **Logout nadia**, ou qualquer nome de usuário que você escolheu ao criar a CloudFormation pilha (ou seja,). *UserPoolUsername*

Vamos enviar algumas mutações *createPicture* para preencher a tabela de fotos. Execute a seguinte consulta do GraphQL dentro do console:

```
mutation {
  createPicture(input:{
    owner: "nadia"
    src: "nadia.jpg"
  }) {
    id
    owner
    src
  }
}
```

A resposta deve ter a aparência abaixo:

```
{
  "data": {
    "createPicture": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "owner": "nadia",
      "src": "nadia.jpg"
    }
  }
}
```

Vamos adicionar mais algumas fotos:

```
mutation {
  createPicture(input:{
    owner: "shaggy"
    src: "shaggy.jpg"
  }) {
    id
    owner
    src
  }
}
```

```
mutation {
  createPicture(input:{
    owner: "rex"
    src: "rex.jpg"
  }) {
    id
    owner
    src
  }
}
```

Você adicionou três fotos usando **nadia** como usuário administrador.

### Mutação createFriendship
<a name="createfriendship-mutation"></a>

Vamos adicionar uma entrada de amizade. Execute as seguintes mutações no console.

Observação: você ainda deve estar conectado como o usuário admin (o usuário admin padrão é **nadia**).

```
mutation {
  createFriendship(id: "nadia", target: "shaggy")
}
```

A resposta deve ter a seguinte aparência:

```
{
  "data": {
    "createFriendship": true
  }
}
```

 **nadia** e **shaggy** são amigos. **rex** não é amigo de ninguém.

### getPicturesByConsulta do proprietário
<a name="getpicturesbyowner-query"></a>

Para esta etapa, faça login como o usuário **nadia** usando os Grupos de usuários do Cognito, com as credenciais configuradas no início desse tutorial. Como usuário **nadia**, recupere as fotos de propriedade do usuário **shaggy**.

```
query {
    getPicturesByOwner(id: "shaggy") {
        id
        owner
        src
    }
}
```

Como **nadia** e **shaggy** são amigos, a consulta deve retornar a foto correspondente.

```
{
  "data": {
    "getPicturesByOwner": [
      {
        "id": "05a16fba-cc29-41ee-a8d5-4e791f4f1079",
        "owner": "shaggy",
        "src": "src://shaggy.jpg"
      }
    ]
  }
}
```

Da mesma forma, se o usuário **nadia** tentar recuperar suas próprias fotos, ele também terá êxito. O resolvedor de pipeline foi otimizado para evitar a execução da GetItem operação **isFriend** nesse caso. Tente a seguinte consulta:

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Se você habilitar o registro em log em sua API (no painel **Configurações**), definir o nível de depuração como **TODOS** e executar a mesma consulta novamente, ele retornará os logs para a execução do campo. Observando os logs, você pode determinar se a função **isFriend** retornou anteriormente no estágio **Modelo de mapeamento da solicitação**:

```
{
  "errors": [],
  "mappingTemplateType": "Request Mapping",
  "path": "[getPicturesByOwner]",
  "resolverArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/types/Query/fields/getPicturesByOwner",
  "functionArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/functions/o2f42p2jrfdl3dw7s6xub2csdfs",
  "functionName": "isFriend",
  "earlyReturnedValue": {
    "owner": "nadia",
    "callerId": "nadia"
  },
  "context": {
    "arguments": {
      "id": "nadia"
    },
    "prev": {
      "result": {
        "owner": "nadia",
        "callerId": "nadia"
      }
    },
    "stash": {},
    "outErrors": []
  },
  "fieldInError": false
}
```

A *earlyReturnedValue*chave representa os dados que foram retornados pela diretiva *\$1return*.

**Finalmente, embora **rex** seja membro do **Viewers** Cognito UserPool Group e porque **rex** não seja amigo de ninguém, ele não poderá acessar nenhuma das fotos de propriedade de **shaggy** ou nadia.** Se você fizer login como **rex** no console e executar a seguinte consulta:

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Você receberá o seguinte erro não autorizado:

```
{
  "data": {
    "getPicturesByOwner": null
  },
  "errors": [
    {
      "path": [
        "getPicturesByOwner"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 9,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access getPicturesByOwner on type Query"
    }
  ]
}
```

Você implementou com sucesso a autorização complexa usando resolvedores de pipeline.

# Usando as operações do Delta Sync em fontes de dados versionadas em AWS AppSync
<a name="tutorial-delta-sync"></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).

Os aplicativos cliente AWS AppSync armazenam dados armazenando em cache as respostas do GraphQL localmente no mobile/web disco de um aplicativo. As fontes de dados e as operações `Sync` versionadas oferecem aos clientes a capacidade de executar o processo de sincronização usando um único resolvedor. Isso permite que os clientes hidratem o cache local com resultados de uma consulta base que possa ter muitos registros e receba apenas os dados alterados desde a última consulta (as *atualizações delta*). Ao permitir que os clientes executem a hidratação base do cache com uma solicitação inicial e atualizações adicionais em outra consulta, você pode mover o cálculo do aplicativo cliente para o back-end. Isso é muito mais eficiente para aplicativos cliente que alternam com frequência entre estados online e offline.

Para implementar a Sincronização delta, a consulta `Sync` usa a operação `Sync` em uma fonte de dados versionada. Quando uma AWS AppSync mutação altera um item em uma fonte de dados versionada, um registro dessa alteração também será armazenado na tabela *Delta*. Você pode optar por usar tabelas *Delta* diferentes (por exemplo, uma por tipo, uma por área de domínio) para outras fontes de dados versionadas ou uma única tabela *Delta* para sua API. AWS AppSync recomenda não usar uma única tabela *Delta* para várias, APIs a fim de evitar a colisão de chaves primárias.

Além disso, os clientes de Sincronização delta também podem receber uma assinatura como um argumento, e a assinatura de coordenadas do cliente se reconecta e realiza gravações entre transições offline para online. A Sincronização delta realiza isso recuperando automaticamente as assinaturas (incluindo recuo exponencial e nova tentativa com tremulação por meio de diferentes cenários de erro de rede) e armazenando eventos em uma fila. A consulta delta ou base apropriada é executada antes de mesclar quaisquer eventos da fila e, finalmente, processar as assinaturas normalmente.

A documentação das opções de configuração do cliente, incluindo o Amplify DataStore, está disponível no site do [Amplify](https://aws-amplify.github.io/) Framework. Essa documentação descreve como configurar operações `Sync` e fontes de dados versionadas do DynamoDB para trabalhar com o cliente de Sincronização delta para obter acesso ideal aos dados.

## Configuração com um clique
<a name="one-click-setup"></a>

Para configurar automaticamente o endpoint do GraphQL AWS AppSync com todos os resolvedores configurados e os AWS recursos necessários, use este modelo: AWS CloudFormation 

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

Esta pilha cria os seguintes recursos na sua conta:
+ 2 Tabelas do DynamoDB (Base e Delta)
+ 1 AWS AppSync API com chave de API
+ 1 Perfil do IAM com política para tabelas do DynamoDB

Duas tabelas são usadas para particionar suas consultas de sincronização em uma segunda tabela que atua como um diário de eventos que foram perdidos quando os clientes estavam offline. Para manter as consultas eficientes na tabela delta, o [ TTLsAmazon](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) DynamoDB é usado para preparar automaticamente os eventos conforme necessário. O tempo TTL é configurável para suas necessidades na fonte de dados (você pode querer que ele tenha 1 hora, 1 dia etc.).

## Schema
<a name="schema"></a>

Para demonstrar o Delta Sync, o aplicativo de amostra cria um esquema *Posts* apoiado por uma tabela *Base* e *Delta* no DynamoDB. AWS AppSync grava automaticamente as mutações nas duas tabelas. A consulta de sincronização obtêm registros da tabela *Base* ou *Delta*, conforme apropriado, e uma única assinatura é definida para mostrar como os clientes podem aproveitar isso em sua lógica de reconexão.

```
input CreatePostInput {
    author: String!
    title: String!
    content: String!
    url: String
    ups: Int
    downs: Int
    _version: Int
}

interface Connection {
  nextToken: String
  startedAt: AWSTimestamp!
}

type Mutation {
    createPost(input: CreatePostInput!): Post
    updatePost(input: UpdatePostInput!): Post
    deletePost(input: DeletePostInput!): Post
}

type Post {
    id: ID!
    author: String!
    title: String!
    content: String!
    url: AWSURL
    ups: Int
    downs: Int
    _version: Int
    _deleted: Boolean
    _lastChangedAt: AWSTimestamp!
}

type PostConnection implements Connection {
    items: [Post!]!
    nextToken: String
    startedAt: AWSTimestamp!
}

type Query {
    getPost(id: ID!): Post
    syncPosts(limit: Int, nextToken: String, lastSync: AWSTimestamp): PostConnection!
}

type Subscription {
    onCreatePost: Post
        @aws_subscribe(mutations: ["createPost"])
    onUpdatePost: Post
        @aws_subscribe(mutations: ["updatePost"])
    onDeletePost: Post
        @aws_subscribe(mutations: ["deletePost"])
}

input DeletePostInput {
    id: ID!
    _version: Int!
}

input UpdatePostInput {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    _version: Int!
}

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

O esquema do GraphQL é padrão, mas vale a pena chamar alguns itens antes de avançar. Primeiro, todas as mutações, automaticamente, primeiro gravam na tabela *Base* e depois na tabela *Delta* . A tabela *Base* é a fonte confiável principal do estado, enquanto a tabela *Delta* é o diário. Se você não passa no `lastSync: AWSTimestamp`, a consulta `syncPosts` é executada na tabela *Base* e hidrata o cache, além de ser executada em períodos como um *processo de recuperação global* para casos de borda quando os clientes ficam off-line por mais tempo que o período de TTL configurado na tabela *Delta*. Se você passa no `lastSync: AWSTimestamp`, a consulta `syncPosts` é executada em sua tabela *Delta* e é usada pelos clientes para recuperar eventos alterados desde a última vez que eles ficaram off-line. Amplificar os clientes passa automaticamente o valor `lastSync: AWSTimestamp` e persiste no disco adequadamente.

O campo *\$1deleted* em *Postagens* é usado para operações **DELETE**. Quando os clientes estão off-line e os registros são removidos da tabela *Base*, esse atributo notifica os clientes que executam a sincronização para remover itens de seu cache local. Nos casos em que os clientes ficam off-line por períodos mais longos e o item foi removido antes que o cliente possa recuperar esse valor com uma consulta de Sincronização delta, o evento de recuperação global na consulta base (configurável no cliente) será executado e remove o item do cache. Esse campo é marcado como opcional porque retorna um valor apenas ao executar uma consulta de sincronização que tenha itens excluídos presentes.

## Mutações
<a name="mutations"></a>

Para todas as mutações, AWS AppSync faz uma Create/Update/Delete operação padrão na tabela *Base* e também registra a alteração na tabela *Delta* automaticamente. Você pode reduzir ou aumentar o tempo para manter registros, modificando o valor `DeltaSyncTableTTL` na fonte de dados. Para organizações com alta velocidade de dados, pode ser adequado manter o tempo reduzido. Como alternativa, se seus clientes estiverem off-line por períodos mais longos, convém manter por mais tempo.

## Consultas de sincronização
<a name="sync-queries"></a>

A *consulta base* é uma operação de sincronização do DynamoDB sem um valor `lastSync` especificado. Para muitas organizações, isso funciona porque a consulta base só é executada na inicialização e periodicamente depois disso.

A *consulta delta* é uma operação de sincronização do DynamoDB com um valor `lastSync` especificado. A *consulta delta* é executada sempre que o cliente volta a ficar online de um estado offline (desde que o período da consulta base não tenha sido acionado para execução). Os clientes rastreiam automaticamente a última vez que executaram com êxito uma consulta para sincronizar dados.

Quando uma consulta delta é executada, o resolvedor da consulta usa o `ds_pk` e o `ds_sk` para consultar somente os registros que foram alterados desde a última sincronização executada pelo cliente. O cliente armazena a resposta apropriada do GraphQL.

Para obter mais informações sobre como executar consultas de sincronização, consulte a [documentação da Operação de sincronização](aws-appsync-conflict-detection-and-sync-sync-operations.md).

## Exemplo
<a name="example"></a>

Vamos começar chamando uma mutação `createPost` para criar um item:

```
mutation create {
  createPost(input: {author: "Nadia", title: "My First Post", content: "Hello World"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

O valor de retorno dessa mutação será o seguinte:

```
{
  "data": {
    "createPost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "My First Post",
      "content": "Hello World",
      "_version": 1,
      "_lastChangedAt": 1574469356331,
      "_deleted": null
    }
  }
}
```

Se examinar o conteúdo da tabela *Base*, você verá um registro semelhante a:

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Se examinar o conteúdo da tabela *Delta*, você verá um registro semelhante a:

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_ttl": {
    "N": "1574472956"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:35:56.331:81d36bbb-1579-4efe-92b8-2e3f679f628b:1"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Agora podemos simular uma consulta *Base* que será executada pelo cliente para hidratar seu armazenamento de dados local usando uma consulta `syncPosts` como:

```
query baseQuery {
  syncPosts(limit: 100, lastSync: null, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
      _lastChangedAt
    }
    startedAt
    nextToken
  }
}
```

O valor de retorno dessa consulta *Base* será o seguinte:

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "My First Post",
          "content": "Hello World",
          "_version": 1,
          "_lastChangedAt": 1574469356331
        }
      ],
      "startedAt": 1574469602238,
      "nextToken": null
    }
  }
}
```

Salvaremos o valor `startedAt` posteriormente para simular uma consulta *Delta*, mas primeiro precisamos fazer uma alteração em nossa tabela. Usaremos a mutação `updatePost` para modificar nossa Postagem existente:

```
mutation updatePost {
  updatePost(input: {id: "81d36bbb-1579-4efe-92b8-2e3f679f628b", _version: 1, title: "Actually this is my Second Post"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

O valor de retorno dessa mutação será o seguinte:

```
{
  "data": {
    "updatePost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "Actually this is my Second Post",
      "content": "Hello World",
      "_version": 2,
      "_lastChangedAt": 1574469851417,
      "_deleted": null
    }
  }
}
```

Se examinar o conteúdo da tabela *Base* agora, você verá o item atualizado:

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Se examinar o conteúdo da tabela *Delta* agora, você verá dois registros:

1. Um registro quando o item foi criado

1. Um registro de quando o item foi atualizado.

O novo item será semelhante a:

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_ttl": {
    "N": "1574473451"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:44:11.417:81d36bbb-1579-4efe-92b8-2e3f679f628b:2"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Agora podemos simular uma consulta *Delta* para recuperar modificações que ocorreram quando um cliente estava offline. Usaremos o valor `startedAt` retornado de nossa consulta *Base* para fazer a solicitação:

```
query delta {
  syncPosts(limit: 100, lastSync: 1574469602238, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
    }
    startedAt
    nextToken
  }
}
```

O valor de retorno dessa consulta *Delta* será o seguinte:

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "Actually this is my Second Post",
          "content": "Hello World",
          "_version": 2
        }
      ],
      "startedAt": 1574470400808,
      "nextToken": null
    }
  }
}
```