

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

# JavaScript tutoriais de resolvedor para AWS AppSync
<a name="tutorials-js"></a>

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.

**Topics**
+ [Criação de um aplicativo de postagem simples usando resolvedores do DynamoDB JavaScript](tutorial-dynamodb-resolvers-js.md)
+ [Usando AWS Lambda resolvedores](tutorial-lambda-resolvers-js.md)
+ [Uso de resolvedores locais](tutorial-local-resolvers-js.md)
+ [Combinação de resolvedores do GraphQL](tutorial-combining-graphql-resolvers-js.md)
+ [Usando resolvedores OpenSearch de serviço](tutorial-elasticsearch-resolvers-js.md)
+ [Execução de transações do DynamoDB](tutorial-dynamodb-transact-js.md)
+ [Uso de operações em lote do DynamoDB](tutorial-dynamodb-batch-js.md)
+ [Uso de resolvedores HTTP](tutorial-http-resolvers-js.md)
+ [Uso do Aurora PostgreSQL com a API de dados](aurora-serverless-tutorial-js.md)

# Criação de um aplicativo de postagem simples usando resolvedores do DynamoDB JavaScript
<a name="tutorial-dynamodb-resolvers-js"></a>

Neste tutorial, você importará suas tabelas do Amazon DynamoDB e as conectará AWS AppSync para criar uma API GraphQL totalmente funcional usando resolvedores de pipeline que você pode JavaScript utilizar em seu próprio aplicativo.

Você usará o AWS AppSync console para provisionar seus recursos do Amazon DynamoDB, criar seus resolvedores e conectá-los às suas fontes de dados. Você também poderá ler e gravar em seu banco de dados do Amazon DynamoDB por meio de instruções do GraphQL e assinar dados em tempo real.

Existem etapas específicas que devem ser concluídas para que as instruções do GraphQL sejam traduzidas para operações do Amazon 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.

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

**Para criar uma API GraphQL no AWS AppSync**

1. Abra o AppSync console e escolha **Criar API**.

1. Selecione **Design do zero** e escolha **Próximo**.

1. Nomeie sua API `PostTutorialAPI` e escolha **Próximo**. Vá para a página de revisão, mantendo o restante das opções definidas com seus valores padrão e escolha `Create`.

O AWS AppSync console cria uma nova API GraphQL para você. Por padrão, ele está usando o modo de autenticação de 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="define-post-api"></a>

Agora que você tem sua API GraphQL, você pode configurar um esquema básico que permite a criação, recuperação e exclusão básica de dados publicados.

**Para adicionar dados ao seu esquema**

1. Na sua API, escolha a guia **Esquema**.

1. Criaremos um esquema que define um tipo `Post` e uma operação `addPost` para adicionar e obter objetos `Post`. 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 esquema**.

## Configuração de sua tabela do Amazon DynamoDB
<a name="configure-dynamodb"></a>

O AWS AppSync console pode ajudar a provisionar os AWS recursos necessários para armazenar seus próprios recursos em uma tabela do Amazon DynamoDB. Nesta etapa, você criará uma tabela do Amazon DynamoDB, para armazenar suas postagens. Você também configurará um [índice secundário](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html) que usaremos posteriormente.

**Para criar sua tabela do Amazon DynamoDB**

1. Na página **Esquema**, escolha **Criar recursos**.

1. Escolha **Usar tipo existente** e depois o tipo `Post`.

1. Na seção **Índices adicionais**, escolha **Adicionar índice**.

1. Nomeie o índice `author-index`.

1. Defina `Primary key` como `author` e a chave `Sort` como `None`.

1. Desative **Automatically generate GraphQL**. Neste exemplo, nós mesmos criaremos o resolvedor.

1. Escolha **Criar**.

Agora você tem uma nova fonte de dados chamada `PostTable`, que pode ser vista em **Fontes de dados** na guia lateral. Você usará essa fonte de dados para vincular suas consultas e mutações à sua tabela do Amazon DynamoDB. 

## Configurando um resolvedor AddPost (Amazon DynamoDB) PutItem
<a name="configure-addpost"></a>

Agora que conhece AWS AppSync a tabela do Amazon DynamoDB, você pode vinculá-la a consultas e mutações individuais definindo resolvedores. O primeiro resolvedor que você cria é o resolvedor de `addPost` pipeline usando JavaScript, que permite criar uma publicação na sua tabela do Amazon DynamoDB. Um resolvedor de pipeline possui os seguintes componentes: 
+ A local no esquema do GraphQL para anexar o resolvedor. Nesse caso, você está configurando um resolvedor no campo `createPost` no tipo `Mutation`. Esse resolvedor será invocado quando o chamador chamar a mutação `{ addPost(...){...} }`. 
+ A fonte de dados a ser usada para esse resolvedor. Nesse caso, você deseja usar a fonte de dados do DynamoDB definida anteriormente para poder adicionar entradas à tabela do DynamoDB de `post-table-for-tutorial`.
+ O manipulador de solicitações. O manipulador de solicitações é uma função que manipula a solicitação recebida do chamador e a traduz em instruções para AWS AppSync execução no DynamoDB.
+ O manipulador de respostas. A tarefa do manipulador de respostas é manipular a resposta do DynamoDB e traduzi-la de volta em algo que o GraphQL espera. 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. 

**Para configurar seu resolvedor**

1. Na sua API, escolha a guia **Esquema**.

1. No painel **Resolvedores**, encontre o campo `addPost` no tipo `Mutation` e depois escolha **Anexar**.

1. Escolha sua fonte de dados e selecione **Criar**.

1. No seu editor de código, substitua o código por este trecho:

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

1. Escolha **Salvar**.

**nota**  
Nesse código, você usa os utilitários do módulo do DynamoDB que permitem criar facilmente solicitações do DynamoDB.

AWS AppSync vem com um utilitário para geração automática de ID chamado`util.autoId()`, que é usado para gerar um ID para sua nova postagem. Se você não especificar uma ID, o utilitário a gerará automaticamente para você.

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

Para obter mais informações sobre os utilitários disponíveis para JavaScript, consulte [recursos de JavaScript tempo de execução para resolvedores e](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) funções. 

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

Agora que o resolvedor foi configurado, é AWS AppSync possível traduzir uma `addPost` mutação recebida em uma operação do Amazon DynamoDB. `PutItem` Agora você pode executar uma mutação para colocar algo na tabela.

**Para executar a operação**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione 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
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `addPost`. Os resultados da publicação recém-criada devem aparecer no painel **Resultados** à direita do painel **Consultas**. 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
       }
     }
   }
   ```

A explicação a seguir mostra o que ocorreu:

1. AWS AppSync recebeu uma solicitação de `addPost` mutação.

1. AWS AppSync executa o manipulador de solicitações do resolvedor. A função `ddb.put` cria uma solicitação `PutItem` semelhante a esta:

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

1. AWS AppSync usa esse valor para gerar e executar uma solicitação do Amazon `PutItem` DynamoDB.

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

1. O manipulador de resposta retorna o resultado imediatamente (`return ctx.result`).

1. O resultado final é visível na resposta do GraphQL.

## Configuração do resolvedor GetPost (Amazon DynamoDB) GetItem
<a name="configure-getpost"></a>

Agora que você pode adicionar dados à tabela do Amazon DynamoDB, é necessário configurar a consulta `getPost` para que ela possa recuperar esses dados da tabela. Para fazer isso, vamos configurar outro resolvedor.

**Para adicionar seu resolvedor**

1. Na sua API, escolha a guia **Esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `getPost` no tipo `Query` e escolha **Anexar**.

1. Escolha sua fonte de dados e selecione **Criar**.

1. No editor de código, substitua o código por este trecho:

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

1. Salve o resolvedor.

**nota**  
Nesse resolvedor, usamos uma expressão de função de seta para o manipulador de resposta.

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

Agora que o resolvedor foi configurado, AWS AppSync sabe como traduzir uma `getPost` consulta recebida em uma operação do Amazon DynamoDB`GetItem`. Agora é possível executar uma consulta para recuperar a publicação criada anteriormente.

**Para executar sua consulta**

1. Na sua API, escolha a guia **Consultas**. 

1. No painel **Consultas**, adicione o seguinte código e use a ID que você copiou após criar sua publicação:

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `getPost`. Os resultados da publicação recém-criada devem aparecer no painel **Resultados** à direita do painel **Consultas**.

1. A publicação recuperada do Amazon DynamoDB deve aparecer no painel **Results** à direita do painel **Queries**. 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
       }
     }
   }
   ```

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 sua função de 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, a função de solicitação pode se parecer com o trecho abaixo:

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

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

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

Você também pode usar um [selectionSetList](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html#aws-appsync-resolver-context-reference-info-js)com `getPost` para representar`expression`:

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

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

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

## Crie uma mutação UpdatePost (Amazon DynamoDB) UpdateItem
<a name="configure-updatepost"></a>

Até agora, você pode criar e recuperar objetos `Post` no Amazon DynamoDB. Depois, você configurará uma nova mutação para atualizar um objeto. Em comparação com a mutação `addPost` que exige que todos os campos sejam especificados, essa mutação permite especificar apenas os campos que deseja alterar. Também introduziu um novo argumento `expectedVersion` que permite especificar a versão que deseja modificar. Você configurará uma condição que garante que você esteja modificando a versão mais recente do objeto. Você fará isso usando o Operation.sc do Amazon DynamoDB `UpdateItem`

**Para criar seu resolvedor**

1. Na sua API, escolha a guia **Esquema**.

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

1. Escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `updatePost`recém-criado no tipo `Mutation` e escolha **Anexar**. Crie seu resolvedor usando o trecho abaixo:

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

1. Salve todas as alterações feitas.

Esse resolvedor usa `ddb.update` para criar uma solicitação `UpdateItem` do Amazon DynamoDB. Em vez de escrever o item inteiro, você está apenas pedindo ao Amazon DynamoDB para atualizar determinados atributos. Isso é feito usando expressões de atualização do Amazon DynamoDB.

A função `ddb.update` recebe uma chave e um objeto de atualização como argumentos. Depois, você verifica os valores dos argumentos recebidos. Quando um valor é definido como `null`, use a operação `remove` do DynamoDB para sinalizar que o valor deve ser removido do item do DynamoDB.

Existe também uma nova seção `condition`. Uma expressão de condição permite que você diga ao AWS AppSync Amazon DynamoDB se a solicitação deve ou não ser bem-sucedida com base no estado do objeto que já está no Amazon 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 Amazon DynamoDB corresponder exatamente ao argumento `expectedVersion`. Quando o item é atualizado, queremos incrementar o valor de `version`. Isso é fácil de fazer com a função de operação `increment`.

Para obter mais informações sobre expressões de condição, consulte a documentação de [expressões de condição](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-condition-expressions).

Para obter mais informações sobre a `UpdateItem` solicitação, consulte a [UpdateItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-updateitem)documentação e a documentação do módulo [DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/built-in-modules-js.html). 

Para obter mais informações sobre como escrever expressões de atualização, consulte a documentação do [ UpdateExpressionsDynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

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

Vamos tentar atualizar o objeto `Post` com o novo resolvedor.

**Para atualizar seu objeto**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione a seguinte mutação. 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: 1
     ) {
       id
       author
       title
       content
       url
       ups
       downs
       version
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `updatePost`.

1. A publicação atualizada no Amazon DynamoDB deve aparecer no painel **Resultados** à direita do painel **Consultas**. 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": 2
       }
     }
   }
   ```

Nessa solicitação, você pediu ao AWS AppSync Amazon DynamoDB que atualizasse somente `title` os campos e. `content` Todos os outros campos foram ignorados (exceto o incremento do 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": null,
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 1RR3QN5F35CS8IV5VR4OQO9NNBVV4KQNSO5AEMVJF66Q9ASUAAJG)"
    }
  ]
}
```

A solicitação falha porque a expressão da condição é avaliada como `false`: 

1. Na primeira vez que você executou a solicitação, o valor do campo `version` da publicação no Amazon DynamoDB era `1`, que correspondia ao argumento `expectedVersion`. A solicitação foi bem-sucedida, o que significa que o campo `version` foi incrementado no Amazon DynamoDB para `2`.

1. Na segunda vez que a solicitação foi executada, o valor do campo `version` da publicação no Amazon DynamoDB era `2`, que não correspondeu ao argumento `expectedVersion`.

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

## Crie mutações de votos (Amazon DynamoDB) UpdateItem
<a name="configure-vote-mutations"></a>

O tipo `Post` contém campos `ups` e `downs` para permitir o registro de votos positivos e negativos. Contudo, no momento, a API não nos permite fazer nada com eles. Vamos adicionar uma mutação para permitir votos positivos e negativos nas publicações.

**Para adicionar sua mutação**

1. Na sua API, escolha a guia **Esquema**.

1. No painel **Esquema**, modifique o tipo `Mutation` e adicione o enumerador `DIRECTION` para adicionar novas mutações de voto:

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

1. Escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `vote`recém-criado no tipo `Mutation` e escolha **Anexar**. Crie um resolvedor criando e substituindo o código pelo seguinte trecho:

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

1. Salve todas as alterações feitas.

### Chamar a API para realizar voto positivo e negativo em uma publicação
<a name="call-api-vote"></a>

Agora que os novos resolvedores foram configurados, AWS AppSync sabe como traduzir uma entrada `upvotePost` ou uma `downvote` mutação em uma operação do Amazon DynamoDB. `UpdateItem` Agora é possível executar mutações para realizar votos positivos ou negativos na publicação criada anteriormente.

**Para executar sua mutação**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione a seguinte mutação. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente:

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `votePost`.

1. A publicação atualizada no Amazon DynamoDB deve aparecer no painel **Resultados** à direita do painel **Consultas**. A aparência deve ser semelhante à seguinte:

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

1. Escolha **Executar** mais algumas vezes. Você deverá ver os campos `ups` e `version` aumentando em `1` cada vez que executar a consulta.

1. Altere a consulta para chamá-la com um arquivo `DIRECTION`.

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `votePost`.

   Desta vez, você deverá ver os campos `downs` e `version` aumentando em `1` cada vez que executar a consulta.

## Configurando um resolvedor DeletePost (Amazon DynamoDB) DeleteItem
<a name="configure-deletepost"></a>

Depois, talvez você deseje criar uma mutação para excluir uma publicação. Você fará isso usando a operação `DeleteItem` do Amazon DynamoDB.

**Para adicionar sua mutação**

1. Em seu esquema, escolha a guia **Esquema**.

1. No painel **Esquema**, modifique o tipo `Mutation` para adicionar uma nova mutação `deletePost`:

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

1. Desta vez, você tornou o campo `expectedVersion` opcional. Depois, escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `delete`recém-criado no tipo `Mutation` e escolha **Anexar**. Crie um resolvedor usando o seguinte código:

   ```
   import { util } from '@aws-appsync/utils'
   
   import { util } from '@aws-appsync/utils';
   import * as ddb from '@aws-appsync/utils/dynamodb';
   
   export function request(ctx) {
     let condition = null;
     if (ctx.args.expectedVersion) {
       condition = {
         or: [
           { id: { attributeExists: false } },
           { version: { eq: ctx.args.expectedVersion } },
         ],
       };
     }
     return ddb.remove({ key: { id: ctx.args.id }, condition });
   }
   
   export function response(ctx) {
     const { error, result } = ctx;
     if (error) {
       util.appendError(error.message, error.type);
     }
     return result;
   }
   ```
**nota**  
O argumento `expectedVersion` é opcional. Se o chamador definir um argumento `expectedVersion` na solicitação, o manipulador de solicitação 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 Amazon 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 Amazon DynamoDB.  
Mesmo que você esteja excluindo um item, é possível retornar o item que foi excluído, caso ainda não tenha sido excluído.

Para obter mais informações sobre a `DeleteItem` solicitação, consulte a [DeleteItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-deleteitem)documentação.

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

Agora que o resolvedor foi configurado, AWS AppSync sabe como traduzir uma `delete` mutação recebida em uma operação do Amazon DynamoDB. `DeleteItem` Agora você pode executar uma mutação para excluir algo na tabela.

**Para executar sua mutação**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione a seguinte mutação. 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
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `deletePost`.

1. A publicação foi excluída do Amazon DynamoDB. **Observe que AWS AppSync retorna o valor do item que foi excluído do Amazon DynamoDB, que deve aparecer no painel Resultados **à** direita do painel Consultas.** 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
       }
     }
   }
   ```

1. O valor só será retornado se essa chamada para `deletePost` for aquela que realmente o exclui do Amazon DynamoDB. Escolha **Executar** novamente.

1. A chamada ainda será bem-sucedida, mas nenhum valor é retornado:

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

1. Agora vamos tentar excluir uma publicação, mas dessa vez especificando um `expectedValue`. Primeiro é necessário criar uma nova publicação, pois você acabou de excluir aquela com a qual estava trabalhando.

1. No painel **Consultas**, adicione 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
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `addPost`.

1. Os resultados da publicação recém-criada devem aparecer no painel **Resultados** à direita do painel **Consultas**. Registre a `id` do objeto recém-criado porque você precisará dele 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
       }
     }
   }
   ```

1. Agora, vamos tentar excluir essa publicação com um valor ilegal para **expectedVersion**. No painel **Consultas**, adicione a seguinte mutação. 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
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `deletePost`. O seguinte resultado é retornado:

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

1. A solicitação falhou porque a expressão da condição foi avaliada como `false`. O valor para `version` da publicação no Amazon 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
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `deletePost`. 

   Dessa vez, a solicitação é bem-sucedida e o valor que foi excluído do Amazon 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
       }
     }
   }
   ```

1. Escolha **Executar** novamente. A chamada ainda é bem-sucedida, mas dessa vez nenhum valor é retornado porque a publicação já estava excluída no Amazon DynamoDB.

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

## Configurar o resolvedor allPost (Scan do Amazon DynamoDB)
<a name="configure-allpost"></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.

**Para adicionar sua mutação**

1. Na sua API, escolha a guia **Esquema**.

1. No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPost` da seguinte forma:

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

1. Adicione um novo tipo `PaginationPosts`:

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

1. Escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `allPost`recém-criado no tipo `Query` e escolha **Anexar**. Crie um resolvedor com o seguinte código:

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

   O manipulador de solicitações desse resolvedor espera dois argumentos opcionais: 
   + `limit` - Especifica o número máximo de itens a serem retornados em uma única chamada.
   + `nextToken` - Usado para recuperar o próximo conjunto de resultados (mostraremos de onde vem o valor `nextToken` mais tarde).

1. Salve todas as alterações feitas no seu resolvedor.

Para obter mais informações sobre solicitação `Scan`, consulte a documentação de referência do [Scan](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-scan).

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

Agora que o resolvedor foi configurado, AWS AppSync sabe como traduzir uma `allPost` consulta recebida em uma operação do Amazon 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.

**Para adicionar e consultar dados **

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione 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 }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja). 

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

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPost`.

   As primeiras cinco publicações devem aparecer no painel **Results** à direita do painel **Queries**. 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": "<token>"
       }
     }
   }
   ```

1. Você recebeu 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(
       limit: 5
       nextToken: "<token>"
     ) {
       posts {
         id
         author
       }
       nextToken
     }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPost`.

   As quatro publicações restantes devem aparecer no painel **Results** à direita do painel **Queries**. 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 um resolvedor de allPostsBy autor (Amazon DynamoDB Query)
<a name="configure-query"></a>

Além de verificar todas as publicações do Amazon DynamoDB, também é possível consultar o Amazon DynamoDB para recuperar as publicações criadas por um determinado autor. A tabela do Amazon DynamoDB que você criou anteriormente já tem um `GlobalSecondaryIndex` chamado `author-index` que pode ser usada com uma operação `Query` do Amazon DynamoDB para recuperar todas as publicações criadas por um autor específico.

**Para adicionar sua consulta**

1. Na sua API, escolha a guia **Esquema**.

1. No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPostsByAuthor` da seguinte forma:

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

   Observe que isso usa o mesmo tipo `PaginatedPosts` usado na consulta `allPost`.

1. Escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `allPostsByAuthor`recém-criado no tipo `Query` e escolha **Anexar**. Crie um resolvedor usando o trecho abaixo:

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

   Assim como o resolvedor `allPost`, esse resolvedor possui dois argumentos opcionais:
   + `limit` - Especifica o número máximo de itens a serem retornados em uma única chamada.
   + `nextToken` - Recupera o próximo conjunto de resultados (o valor `nextToken` pode ser obtido em uma chamada anterior).

1. Salve todas as alterações feitas no seu resolvedor.

Para obter mais informações sobre a solicitação `Query`, consulte a documentação de referência da [consulta](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-query).

### Chamar a API para consultar todas as publicações por autor​
<a name="call-api-query"></a>

Agora que o resolvedor foi configurado, 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 disso, no entanto, vamos preencher a tabela com mais algumas publicações pois até agora todas têm o mesmo autor.

**Para adicionar dados e consultar**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, adicione 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 }
   }
   ```

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `addPost`.

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPostsByAuthor`. Todas as publicações de autoria de `Nadia` devem aparecer no painel **Resultados** à direita do painel **Consultas**. 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
       }
     }
   }
   ```

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

1. No painel **Consultas**, cole a seguinte consulta: 

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPostsByAuthor`. Todas as publicações de autoria de `AUTHORNAME` devem aparecer no painel **Resultados** à direita do painel **Consultas**. 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": "<token>"
       }
     }
   }
   ```

1. Atualize o argumento `nextToken` com o valor retornado pela consulta anterior da seguinte forma:

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPostsByAuthor`. As publicações restantes de autoria de `AUTHORNAME` devem aparecer no painel **Resultados** à direita do painel **Consultas**. 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. Também é possível modelar objetos complexos com seu resolvedor, como conjuntos, listas e mapas. Vamos atualizar o tipo `Post` para incluir tags. Uma publicação pode ter zero 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.

**Para configurar seus dados**

1. Na sua API, escolha a guia **Esquema**. 

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

1. No painel **Esquema**, modifique o tipo `Query` para adicionar uma nova consulta `allPostsByTag` da seguinte forma:

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

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

1. Escolha **Salvar esquema**.

1. No painel **Resolvedores** à direita, encontre o campo `allPostsByTag`recém-criado no tipo `Query` e escolha **Anexar**. Crie seu resolvedor usando o trecho abaixo:

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

1. Salve todas as alterações feitas no seu resolvedor.

1. Agora, faça o mesmo para o campo `Mutation` `addTag` usando o trecho abaixo:
**nota**  
Embora os utilitários do DynamoDB atualmente não sejam compatíveis com operações de conjuntos, você ainda pode interagir com os conjuntos criando a solicitação sozinho.

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

1. Salve todas as alterações feitas no seu resolvedor.

1. Repita isso mais uma vez para o campo `Mutation` `removeTag` usando o trecho abaixo:

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

1. Salve todas as alterações feitas no seu resolvedor.

### Chamar a API para trabalhar com tags
<a name="call-api-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`.

**Para usar tags**

1. Na sua API, escolha a guia **Consultas**.

1. No painel **Consultas**, cole a seguinte consulta:

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPostsByAuthor`.

1. Todas as publicações de Nadia devem aparecer no painel **Resultados** à direita do painel **Consultas**. 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
       }
     }
   }
   ```

1. Vamos usar aquela com o título *The cutest dog in the world*. Registre sua `id` porque você a usará mais tarde. Agora, vamos tentar adicionar uma tag `dog`.

1. No painel **Consultas**, adicione a seguinte mutação. Também será necessário atualizar o argumento `id` para o valor anotado anteriormente.

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `addTag`. A publicação é atualizada com a nova tag:

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

1. É possível adicionar mais tags. Atualize a mutação para alterar o argumento `tag` para `puppy`:

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `addTag`. A publicação é atualizada com a nova tag:

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

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

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `removeTag`. A publicação é atualizada e a tag `puppy` é excluída.

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

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

1. Escolha **Executar** (o botão de reprodução laranja) e selecione `allPostsByTag`. 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
       }
     }
   }
   ```

## Conclusão
<a name="conclusion-dynamodb-tutorial-js"></a>

Neste tutorial, você criou uma API que permite manipular objetos `Post` no DynamoDB usando AWS AppSync e GraphQL. 

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

Para excluir a função associada à tabela do DynamoDB, selecione sua fonte de dados na tabela **Fontes de dados** e clique em **editar**. Observe o valor da função em **Criar ou usar uma função existente**. Acesse o console do IAM, para excluir a função.

Para excluir sua tabela do DynamoDB, clique no nome da tabela na lista de fontes de dados. Isso leva você ao console do DynamoDB, onde você pode excluir a tabela. 

# Usando AWS Lambda resolvedores em AWS AppSync
<a name="tutorial-lambda-resolvers-js"></a>

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.

## Ferramentas elétricas para AWS Lambda
<a name="powertools-graphql"></a>

O manipulador de eventos Powertools for AWS Lambda GraphQL simplifica o roteamento e o processamento de eventos do GraphQL nas funções Lambda. Ele está disponível para Python e Typescript. Leia mais sobre o manipulador de eventos da API GraphQL no Powertools para obter a documentação do AWS Lambda , consulte as referências a seguir.
+ [Powertools para manipulador de eventos AWS Lambda GraphQL (Python)](https://docs.aws.amazon.com/powertools/python/latest/core/event_handler/appsync/)
+ [Powertools para manipulador de eventos AWS Lambda GraphQL (texto datilografado)](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-graphql/) 

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

O exemplo a seguir mostra uma função do Lambda escrita em `Node.js`(runtime: Node.js 18.x) que executa diferentes operações em publicações de blog como parte de um aplicativo de publicações de blog. Observe que o código deve ser salvo em um nome de arquivo com extensão .mis.

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

  const posts = {
1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

  const relatedPosts = {
1: [posts['4']],
    2: [posts['3'], posts['5']],
    3: [posts['2'], posts['1']],
    4: [posts['2'], posts['1']],
    5: [],
  }

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

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. 

**nota**  
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 AWS Management Console.

## Configurar a fonte de dados para o Lambda
<a name="configure-data-source-for-lamlong-js"></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 da sua função. Para **ARN da função**, 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-js"></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-js"></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.

Você criará um resolvedor que usa o tempo de execução AWS AppSync JavaScript (`APPSYNC_JS`) e interagirá com suas funções do Lambda. Para saber mais sobre como escrever AWS AppSync resolvedores e funções com JavaScript, consulte [recursos de JavaScript tempo de execução para resolvedores e](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) funções.

Para obter mais informações sobre os modelos de mapeamento do Lambda, consulte a [referência da função de JavaScript resolução para o Lambda](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-lambda-js.html).

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 painel **Resolvedores**, escolha **Anexar** ao lado do campo. `getPost(id:ID!): Post` Escolha sua fonte de dados do Lambda. Depois, forneça o seguinte código:

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

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

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

Esse código do resolvedor passa o nome do campo, a lista de argumentos e o contexto sobre o objeto de origem para a função do Lambda quando ela o invoca. Escolha **Salvar**.

Você anexou seu primeiro resolvedor com sucesso. Repita essa operação para os campos restantes. 

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

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

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

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

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

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

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

Qualquer resolução de campo determinada pode resultar em um erro. Com AWS AppSync, você pode gerar erros das seguintes fontes:
+ Manipulador de respostas do resolvedor
+ Função do Lambda

### No manipulador de respostas do resolvedor
<a name="from-the-resolver-response-handler-js"></a>

Para gerar erros intencionais, você pode usar o método utilitário `util.error`. 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 manipulador de respostas do resolvedor `Post.relatedPosts: [Post]`.

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

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

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

O erro é recebido em seu manipulador de respostas. Você pode enviá-lo de volta na resposta do GraphQL anexando o erro à resposta com `util.appendError`. Para fazer isso, altere seu manipulador de resposta de AWS AppSync função para isso:

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

Isso retorna uma resposta do GraphQL semelhante à seguinte:

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

## Caso de uso avançado: agrupamento em lotes
<a name="advanced-use-case-batching-js"></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 {   // 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 {   // 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 atualizar o resolvedor para `relatedPosts` lidar com lotes.

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

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

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

O código agora altera a operação de `Invoke` para `BatchInvoke` quando `fieldName` está sendo resolvido é `relatedPosts`. Agora, habilite o lote na função na seção **Configurar lotes**. Defina o tamanho máximo do lote definido como `5`. Escolha **Salvar**.

Com essa alteração, ao resolver `relatedPosts`, a função do Lambda recebe como entrada o seguinte:

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

Quando `BatchInvoke` for especificado na 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
]
```

Você pode atualizar seu código do Lambda para lidar com lotes para `relatedPosts`:

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

  const posts = {
    1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

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

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

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

Os exemplos anteriores mostram que é possível retornar um único erro da função do Lambda ou gerar um erro do seu manipulador de respostas. 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:

```
export const handler = async (event) => {
console.log('Received event {}', JSON.stringify(event, 3))
  // throw new Error('I fail. always')
const posts = {
1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', },
    2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', },
    3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
    4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', },
    5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', },
  }

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

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

Atualize o código do resolvedor `relatedPosts`:

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

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

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

O manipulador de respostas agora verifica erros retornados pela função do Lambda nas operações `Invoke`, verifica erros retornados para itens individuais das operações `BatchInvoke` e, por fim, verifica `fieldName`. Para `relatedPosts`, a função retorna `result.data`. Para todos os outros campos, a função retorna apenas `result`. Por exemplo, veja a consulta abaixo:

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

Essa consulta retorna uma resposta do GraphQL semelhante à seguinte:

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

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

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

# Uso de resolvedores locais no AWS AppSync
<a name="tutorial-local-resolvers-js"></a>

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 manipulador de solicitação para o manipulador de resposta. A resolução do campo não deixará o AWS AppSync.

Os resolvedores locais são úteis em uma infinidade de situações. 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 pub/sub no qual os usuários possam publicar e assinar mensagens. 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).

## Criação do aplicativo pub/sub
<a name="create-the-pub-sub-application-js"></a>

Primeiro, crie uma API GraphQL em branco escolhendo a opção **Design do zero** e configurando os detalhes opcionais ao criar sua API GraphQL.

Em nosso aplicativo pub/sub, os clientes podem assinar e publicar mensagens. Cada mensagem publicada inclui um nome e dados. Adicione-o ao esquema:

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

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

type Query {
	getChannel: Channel
}

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

Depois, vamos anexar um resolvedor ao campo `Mutation.publish`. No painel **Resolvedores** próximo ao painel **Esquema**, encontre o tipo `Mutation`, o campo `publish(...): Channel` e clique em **Anexar**.

Crie uma fonte de dados *Nenhum* e nomeie-a como *PageDataSource*. Anexe-a ao seu resolvedor.

Adicione a implementação do seu resolvedor usando o seguinte trecho:

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

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

Certifique-se de criar o resolvedor e salvar as alterações feitas.

## Enviar e assinar mensagens
<a name="send-and-subscribe-to-messages-js"></a>

Para que os clientes recebam mensagens, primeiro eles devem assinar uma caixa de entrada.

No painel **Consultas**, execute a assinatura `SubscribeToData`:

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

 O assinante receberá mensagens sempre que a mutação `publish` for invocada, mas somente quando a mensagem for enviada para a assinatura `channel`. Vamos tentar isso no painel **Consultas**. Enquanto sua assinatura ainda estiver em execução no console, abra outro console e execute a seguinte solicitação no painel **Consultas**:

**nota**  
Estamos usando strings JSON válidas neste exemplo.

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

O resultado será semelhante a este:

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

Acabamos de demonstrar o uso de resolvedores locais, publicando uma mensagem e recebendo-a sem sair do serviço AWS AppSync.

# Combinando resolvedores GraphQL em AWS AppSync
<a name="tutorial-combining-graphql-resolvers-js"></a>

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 seus tipos do GraphQL por meio de diferentes fontes de dados, permitindo misturar e combinar um esquema para melhor atender às suas necessidades.

Os cenários a seguir demonstram como misturar e combinar fontes de dados no seu esquema. Antes de começar, você deve estar familiarizado com a configuração de fontes de dados e resolvedores para o Amazon AWS Lambda DynamoDB e o Amazon Service. OpenSearch 

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

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

```
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, você teria um total de seis resolvedores, cada um precisando de uma fonte de dados. Uma forma de resolver esse problema seria conectá-los a uma única tabela do Amazon DynamoDB, `Posts` chamada, na qual o campo executa uma varredura e `AllPost` o campo executa uma consulta ([JavaScriptconsulte `searchPosts` a referência da função de resolução](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html) do DynamoDB). No entanto, você não está limitado ao Amazon DynamoDB; existem fontes de dados diferentes, como Lambda OpenSearch ou Service, para atender às suas necessidades comerciais. 

## Alteração de dados por meio de resolvedores
<a name="alter-data-through-resolvers-js"></a>

Talvez seja necessário retornar resultados de um banco de dados de terceiros que não seja diretamente suportado por fontes AWS AppSync de dados. Talvez você também precise realizar modificações complexas nos dados antes que eles sejam retornados aos clientes da API. Isso pode ser causado pela formatação inadequada dos tipos de dados, como diferenças de carimbo de data/hora nos clientes ou pelo tratamento de problemas de compatibilidade com versões anteriores. Nesse caso, conectar AWS Lambda funções como fonte de dados à sua AWS AppSync API é a solução apropriada. Para fins ilustrativos, no exemplo a seguir, uma AWS Lambda função manipula dados obtidos de um armazenamento de dados de terceiros:

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

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

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

Essa é uma função do Lambda perfeitamente válida e pode ser anexada ao campo `AllPost` 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-js"></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 Você pode simplesmente anexar o `searchPosts` resolvedor à fonte de dados do OpenSearch 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.

Para começar a usar essas fontes de dados específicas, consulte nossos tutoriais do [DynamoDB](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-resolvers-js.html) e [Lambda](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers-js.html).

Por exemplo, usando o esquema do nosso tutorial anterior, a seguinte mutação adiciona um item ao DynamoDB:

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

Isso grava dados no DynamoDB, que então transmite dados via Lambda para o OpenSearch Amazon Service, que você usa para pesquisar publicações 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
    }
}

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

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 `allPost{...}` e `getPost{...}`. Essa pilha usa o seguinte código de exemplo para transmissões do DynamoDB:

**nota**  
Este código Python é um exemplo e não deve ser usado em código de produção.

```
import boto3
import requests
from requests_aws4auth import AWS4Auth

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

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

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

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

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

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 resolvedores OpenSearch do Amazon Service em AWS AppSync
<a name="tutorial-elasticsearch-resolvers-js"></a>

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 nossa [referência JavaScript de função de resolução para OpenSearch](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-elasticsearch-js.html).

## Criar um novo domínio OpenSearch de serviço
<a name="create-a-new-es-domain-js"></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 US-West-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/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 uma fonte de dados para o OpenSearch serviço
<a name="configure-data-source-for-es-js"></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**. Escolha **Criar fonte de dados** e insira um nome amigável para a fonte de dados, como “*oss*”. Em seguida, escolha o ** OpenSearch 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 atribuir AWS AppSync as permissões apropriadas à função ou escolher uma função existente, que tenha a seguinte política embutida:

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ê deve adicionar uma política semelhante à abaixo 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 AWS AppSync dados, que pode ser encontrada no console do IAM se você permitir que o console a crie.

## Conexão de um resolvedor
<a name="connecting-a-resolver-js"></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:

```
 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. No painel **Resolvedores**, encontre `searchPosts` e escolha **Anexar**. Escolha sua fonte OpenSearch de dados de serviço e salve o resolvedor. Atualize o código do seu resolvedor usando o trecho abaixo:

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

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

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

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.

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

O manipulador de solicitação do resolvedor 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 **esquemas** do AWS AppSync console, adicione uma `allPostsByAuthor` consulta:

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

No painel **Resolvedores**, encontre `allPostsByAuthor` e escolha **Anexar**. Escolha a fonte OpenSearch de dados do Serviço e use o código a seguir:

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

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

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

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 usar informações pré-preenchidas, como texto padrão.

## Adicionando dados ao OpenSearch serviço
<a name="adding-data-to-es-js"></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 pode servir como um mecanismo para notificar os clientes sobre atualizações nos dados em seu domínio de serviço. OpenSearch 

Volte para a página **Esquema** no AWS AppSync console e selecione **Anexar** para a `addPost()` mutação. Selecione a fonte OpenSearch de dados do serviço novamente e use o código a seguir:

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

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

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

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`. Este exemplo também mostra como usar `context.arguments`, que também pode ser escrito como `ctx.args`, em seu manipulador de solicitações.

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

**Por fim, se você quiser usar a `getPost(id:ID)` consulta em seu esquema para retornar um documento individual, localize essa consulta no editor de **esquemas** do AWS AppSync console e escolha Anexar.** Selecione a fonte OpenSearch de dados do serviço novamente e use o código a seguir:

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

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

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

## Executar consultas e mutações
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations-js"></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 search {
    searchPosts {
        id
        title
        author
        content
    }
}
```

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

# Executando transações do DynamoDB no AWS AppSync
<a name="tutorial-dynamodb-transact-js"></a>

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
+ Gravando registros em transações 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-js"></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/*"
            ]
        }
    ]
}
```

------

**nota**  
As funções são vinculadas às fontes de dados em AWS AppSync, e os resolvedores nos campos são invocados em uma fonte de dados. As fontes de dados configuradas para buscar no DynamoDB têm apenas uma tabela especificada, para manter a configurações 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 código de resolvedor, descrita abaixo.

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

Para simplificar, usaremos a mesma fonte de dados para todos os resolvedores usados neste tutorial. 

Teremos duas tabelas chamadas **savingAccounts** e **checkingAccounts**, ambas com `accountNumber` como uma chave de partição, e uma tabela **transactionHistory** com `transactionId` como chave de partição. É possível usar os comandos da CLI abaixo para criar suas tabelas. Substitua `region` pela região.

**Com a CLI**

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

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

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

No AWS AppSync console, em **Fontes de dados**, crie uma nova fonte de dados do DynamoDB e nomeie-a. **TransactTutorial** Selecione **savingAccounts** como tabela (a tabela especificada não importa ao usar transações). Selecione para criar uma fonte de dados. Você pode revisar a configuração da fonte de dados para ver o nome da função gerada. No console do IAM, você pode adicionar uma política em linha que permite que a fonte de dados interaja com todas as tabelas.

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

### TransactWriteItems - Preencher contas
<a name="transactwriteitems-populate-accounts-js"></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`. Escolha a fonte de dados do `TransactTutorial` e escolha **Criar**.

Agora use o seguinte código:

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

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

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

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

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

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-js"></a>

Anexe um resolvedor à mutação `transferMoney` com o código a seguir. Para cada transferência, precisamos de um modificador de sucesso para contas correntes e de poupança, e precisamos rastrear a transferência nas transações.

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

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

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

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

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

Agora, navegue até a seção **Consultas** do AWS AppSync console 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 três 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-js"></a>

Para recuperar os detalhes das contas poupança e corrente em uma única solicitação transacional, anexaremos um resolvedor à operação `Query.getAccounts` do GraphQL em nosso esquema. Selecione **Anexar** e escolha a mesma fonte de dados do `TransactTutorial`criada no início do tutorial. Use o seguinte código: 

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

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

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

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

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

Salve o resolvedor e navegue até as seções **Consultas** do console do AWS AppSync . Para recuperar as contas poupança e corrente, 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 operações em lote do DynamoDB em AWS AppSync
<a name="tutorial-dynamodb-batch-js"></a>

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, 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
+ Gravar registros em lote em uma ou mais tabelas
+ Gravar ou excluir registros condicionalmente em várias tabelas que podem ter uma relação

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 que o resolvedor acessará.
+ A especificação de tabela para um resolvedor faz parte do objeto de solicitação.

## Lotes de tabela única
<a name="single-table-batch-js"></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 começar, vamos criar uma API GraphQL. No AWS AppSync console, escolha **Create API**, **GraphQL APIs** e **Design from scratch**. Nomeie sua API `BatchTutorial API`, escolha **Próximo** e, na etapa **Especificar recursos do GraphQL**, selecione **Criar recursos do GraphQL mais tarde** e clique em **Próximo**. Revise seus detalhes e crie a API. Acesse a página **Esquema** e cole 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]
}
```

Salve seu esquema e escolha **Criar recursos** na parte superior da página. Escolha **Usar tipo existente** e selecione o tipo `Post`. Nomeie sua tabela `Posts`. Certifique-se de que a **Chave primária** esteja definida como `id`, desmarque **Gerar GraphQL automaticamente** (você fornecerá seu próprio código) e selecione **Criar**. Para começar, AWS AppSync cria uma tabela do DynamoDB e uma fonte de dados conectada à tabela com as funções apropriadas. No entanto, ainda há algumas permissões que você precisa adicionar à função. Acesse a página **Fontes de dados** e escolha a nova fonte de dados. Em **Selecionar uma função existente**, você notará que uma função foi criada automaticamente para a tabela. Anote a função (deve ter uma aparência parecida com`appsync-ds-ddb-aaabbbcccddd-Posts`) e, em seguida, acesse o console do IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). No console do IAM, escolha **Funções** e selecione sua função na tabela. Na sua função, em **Políticas de permissões**, clique em "`+`" ao lado da política (deve ter um nome semelhante ao nome da função). Escolha **Editar** na parte superior do expansível quando a política aparece. Você precisa adicionar permissões em lote à sua política, especificamente `dynamodb:BatchGetItem` e `dynamodb:BatchWriteItem`. Essa lista será semelhante a:

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

****  

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

------

Escolha **Avançar** e **Salvar alterações**. Sua política deve permitir o processamento em lote agora.

De volta ao AWS AppSync console, acesse a página **Esquema** e selecione **Anexar** ao lado do `Mutation.batchAdd` campo. Crie seu resolvedor usando a tabela `Posts` como fonte de dados. No editor de código, substitua os manipuladores pelo trecho abaixo. Esse trecho recebe automaticamente cada item no tipo `input PostInput` do GraphQL e cria um mapa, que é necessário para a operação `BatchPutItem`:

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

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

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

Navegue até a página **Consultas** do AWS AppSync console e execute a seguinte `batchAdd` mutação:

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

Você deverá ver os resultados impressos na tela; isso pode ser validado revisando o console do DynamoDB para verificar os valores gravados na tabela `Posts`.

Em seguida, repita o processo de anexar um resolvedor, mas para o campo `Query.batchGet` usando a tabela `Posts` como fonte de dados. Substitua os manipuladores pelo código abaixo. Isso recebe automaticamente cada item no tipo `ids:[]` do GraphQL e cria um mapa, que é necessário para a operação `BatchGetItem`:

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

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

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

Agora, volte para a página **Consultas** do AWS AppSync console e execute a seguinte `batchGet` consulta:

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

Por fim, anexe mais um resolvedor ao campo `Mutation.batchDelete` usando a tabela `Posts` como fonte de dados.​ Substitua os manipuladores pelo código abaixo. Isso recebe automaticamente cada item no tipo `ids:[]` do GraphQL e cria um mapa, que é necessário para a operação `BatchGetItem`:

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

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

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

Agora, volte para a página de **consultas** do AWS AppSync console e execute a seguinte `batchDelete` mutação:

```
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-js"></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 para animais de estimação em que sensores informam a localização e a temperatura corporal do animal. 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.

No AWS AppSync console, escolha **Create API**, **GraphQL APIs** e **Design from scratch**. Nomeie sua API `MultiBatchTutorial API`, escolha **Próximo** e, na etapa **Especificar recursos do GraphQL**, selecione **Criar recursos do GraphQL mais tarde** e clique em **Próximo**. Revise seus detalhes e crie a API. Vá até a página **Esquema**, cole e salve o seguinte esquema:

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

Precisamos criar duas tabelas do DynamoDB:
+ `locationReadings` armazenará as leituras de localização do sensor.
+ `temperatureReadings` armazenará as leituras de temperatura do sensor.

Ambas as tabelas compartilharão a mesma estrutura de chave primária: `sensorId (String)` como chave de partição e `timestamp (String)` como chave de classificação.

Escolha **Criar recursos** na parte superior da página. Escolha **Usar tipo existente** e selecione o tipo `locationReadings`. Nomeie sua tabela `locationReadings`. Certifique-se de que a **Chave primária** esteja definida como `sensorId` e a chave de classificação como `timestamp`. Desmarque **Gerar GraphQL automaticamente** (você fornecerá seu próprio código) e selecione **Criar**. Repita esse processo para `temperatureReadings` usar `temperatureReadings` como tipo e nome da tabela. Use as mesmas teclas acima.

Suas novas tabelas conterão funções geradas automaticamente. Ainda existem algumas permissões que você precisa adicionar a essas funções. Vá para a página **Fontes de dados** e escolha `locationReadings`. Em **Selecionar uma função existente**, é possível ver a função. Anote a função (deve ter uma aparência parecida com`appsync-ds-ddb-aaabbbcccddd-locationReadings`) e, em seguida, acesse o console do IAM ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)). No console do IAM, escolha **Funções** e selecione sua função na tabela. Na sua função, em **Políticas de permissões**, clique em "`+`" ao lado da política (deve ter um nome semelhante ao nome da função). Escolha **Editar** na parte superior do expansível quando a política aparece. Você precisa adicionar permissões a essa política. Essa lista será semelhante a:

Escolha **Avançar** e **Salvar alterações**. Repita esse processo para a fonte de dados `temperatureReadings` usando o mesmo trecho de política acima.

### BatchPutItem - Gravação de leituras do sensor
<a name="batchputitem-recording-sensor-readings-js"></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. Precisaremos adicionar um resolvedor a esse campo.

Na página **Esquema** do AWS AppSync console, selecione **Anexar** ao lado do `Mutation.recordReadings` campo. Na próxima tela, crie seu resolvedor usando a tabela `locationReadings` como fonte de dados.

Depois de criar seu resolvedor, substitua os manipuladores pelo código a seguir no editor. Essa operação `BatchPutItem` nos permite especificar múltiplas tabelas: 

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

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

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

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

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

**nota**  
O uso de `utils.appendError()` é semelhante ao `util.error()`, com a principal diferença de que a avaliação do manipulador de solicitação ou resposta não é interrompida. Em vez disso, sinaliza que houve um erro no campo, mas permite que o manipulador seja avaliado e, consequentemente, retorne os 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** no AWS AppSync console. Agora podemos enviar algumas leituras do sensor.

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 se os dados aparecem nas tabelas `locationReadings` e `temperatureReadings`.

### BatchDeleteItem - Excluindo as leituras do sensor
<a name="batchdeleteitem-deleting-sensor-readings-js"></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. Na página **Esquema** do AWS AppSync console, selecione **Anexar** ao lado do `Mutation.deleteReadings` campo. Na próxima tela, crie seu resolvedor usando a tabela `locationReadings` como fonte de dados.

Depois de criar seu resolvedor, substitua os manipuladores no editor de código pelo trecho abaixo. Neste resolvedor, usamos um mapeador de função auxiliar que extrai `sensorId` e `timestamp` das entradas fornecidas. 

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

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

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

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

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

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

**nota**  
Ao contrário da operação `DeleteItem`, o item totalmente excluído não é retornado na resposta. Somente a chave passada é retornada. Para saber mais, consulte a [referência da função BatchDeleteItem no JavaScript resolvedor do DynamoDB.](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-batch-delete-item)

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-js"></a>

Outra operação comum para o nosso aplicativo seria recuperar as leituras de um sensor em um determinado momento. Vamos anexar um resolvedor ao campo do GraphQL `Query.getReadings` em nosso esquema. Na página **Esquema** do AWS AppSync console, selecione **Anexar** ao lado do `Query.getReadings` campo. Na próxima tela, crie seu resolvedor usando a tabela `locationReadings` como fonte de dados.

Vamos usar o seguinte código: 

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

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

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

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

Salve o resolvedor e navegue até a página **Consultas** no AWS AppSync console. Agora, vamos recuperar as leituras dos nossos sensores.

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-js"></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 o tratamento de erros é inerentemente específico do aplicativo, AWS AppSync você tem a oportunidade de lidar com erros no manipulador de respostas. 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`. No manipulador de respostas, é possível manipular resultados parciais de três maneiras:

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

1. Gere um erro (usando `util.error(...)`) interrompendo a avaliação do manipulador, que não retornará nenhum dado.

1. Anexe um erro (usando `util.appendError(...)`) e também retorne 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-js"></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 durante a segunda tentativa AWS AppSync de processar os elementos restantes no lote.

O JSON a seguir representa o contexto serializado após a invocação em lote do DynamoDB, mas antes do manipulador de resposta ser chamado:

```
{
  "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 e AWS AppSync 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.

**nota**  
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-js"></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 código que gravamos é familiar e concentra-se apenas nos dados do resultado.

**Manipulador de resposta**

```
export function response(ctx) {
  return 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. Gerar um erro para abortar a execução do manipulador de resposta
<a name="raising-an-error-to-abort-the-response-execution-js"></a>

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

**Código do manipulador de respostas**

```
export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return 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-js"></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 manipulador de respostas. Depois de avaliar o manipulador de respostas, AWS AppSync processará quaisquer erros de contexto anexando-os ao bloco de erros da resposta do GraphQL.

**Código do manipulador de respostas**

```
export function response(ctx) {
  if (ctx.error) {
    util.appendError(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return 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. (...)"
    }
  ]
}
```

# Usando resolvedores HTTP em AWS AppSync
<a name="tutorial-http-resolvers-js"></a>

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 .

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

1. Abra o AWS AppSync console e escolha **Criar API**.

1. Escolha **GraphQL APIs** e, em seguida, escolha **Design do zero**. Escolha **Próximo**.

1. Para o nome da API, digite `UserData`. Escolha **Próximo**.

1. Selecione `Create GraphQL resources later`. Escolha **Próximo**.

1. Revise suas entradas e selecione **Criar API**.

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 ainda mais sua API GraphQL e executar solicitações.

## 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, use o trecho abaixo:

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

1. Na página **Fontes de dados** na sua API AWS AppSync GraphQL, escolha **Criar fonte de dados**.

1. Insira um nome para a fonte de dados como `HTTP_Example`.

1. Em **Tipo de fonte de dados**, escolha **Endpoint de HTTP**.

1. Configure o endpoint para o endpoint da API Gateway que foi criado no início do tutorial. Você pode encontrar seu endpoint gerado pela pilha navegando até o console do Lambda e encontrando seu aplicativo em **Aplicativos**. Dentro das configurações do seu aplicativo, você verá um **endpoint de API** que será seu endpoint no AWS AppSync. Lembre-se de não incluir o nome do estágio como parte do endpoint. Por exemplo, se o seu endpoint fosse `https://aaabbbcccd.execute-api.us-east-1.amazonaws.com/v1`, você digitaria `https://aaabbbcccd.execute-api.us-east-1.amazonaws.com`.

**nota**  
No momento, somente endpoints públicos são suportados pelo AWS AppSync.  
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).

## Configuração de resolvedores do
<a name="configuring-resolvers"></a>

Nesta etapa, você conectará a fonte de dados HTTP às consultas `getUser` e `addUser`.

Como configurar o resolvedor `getUser`:

1. Na sua API do AWS AppSync GraphQL, escolha a guia **Esquema**.

1. À direita do editor **Esquema**, no painel **Resolvedores** e em tipo **Consulta**, encontre o campo `getUser` e escolha **Anexar**.

1. Mantenha o tipo de resolvedor como `Unit` e o runtime como `APPSYNC_JS`.

1. Em **Nome da fonte de dados**, escolha o endpoint HTTP que você criou anteriormente.

1. Escolha **Criar**.

1. No editor de código **Resolvedor**, adicione o seguinte trecho como seu manipulador de solicitação:

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

1. Adicione o seguinte trecho como seu manipulador de respostas:

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

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

Como configurar o resolvedor `addUser`:

1. Escolha a guia **Esquema**.

1. À direita do editor **Esquema**, no painel **Resolvedores** e em tipo **Consulta**, encontre o campo `addUser` e escolha **Anexar**.

1. Mantenha o tipo de resolvedor como `Unit` e o runtime como `APPSYNC_JS`.

1. Em **Nome da fonte de dados**, escolha o endpoint HTTP que você criou anteriormente.

1. Escolha **Criar**.

1. No editor de código **Resolvedor**, adicione o seguinte trecho como seu manipulador de solicitação:

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

1. Adicione o seguinte trecho como seu manipulador de respostas:

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

1. Escolha a guia **Consulta** e, depois, execute a seguinte consulta:

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

   Se você executar a consulta `getUser` novamente, ela deverá retornar a seguinte resposta:

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

## Invocando serviços AWS
<a name="invoking-aws-services-js"></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 PostgreSQL com a API de dados em AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Saiba como conectar sua API GraphQL aos bancos de dados Aurora PostgreSQL usando o. AWS AppSync Essa integração permite que você crie aplicativos escaláveis e orientados por dados executando consultas e mutações SQL por meio de operações do GraphQL. AWS AppSync fornece uma fonte de dados para executar instruções SQL em clusters do Amazon Aurora que são habilitados com uma API de dados. Você pode usar AWS AppSync resolvedores para executar instruções SQL na API de dados com consultas, mutações e assinaturas do GraphQL.

Antes de começar este tutorial, você deve ter familiaridade básica com AWS serviços e conceitos do GraphQL.

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

**Topics**
+ [Configurar o banco de dados Aurora PostgreSQL](#creating-clusters)
+ [Criar o banco de dados e uma tabela](#creating-db-table)
+ [Criar um esquema do GraphQL](#rds-graphql-schema)
+ [Resolvedores para RDS](#rds-resolvers)
+ [Excluir o cluster](#rds-delete-cluster)

## Configurar o banco de dados Aurora PostgreSQL
<a name="creating-clusters"></a>

Antes de adicionar uma fonte de dados do Amazon RDS AWS AppSync, faça o seguinte.

1. Ative uma API Data em um cluster do Aurora Serverless v2.

1. Configure um segredo usando AWS Secrets Manager

1. Crie o cluster usando o AWS CLI comando a seguir.

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

Isso retornará um ARN para o cluster. Depois de criar um cluster, você deve adicionar uma instância Serverless v2 com o comando a seguir. AWS CLI 

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

**nota**  
Estes endpoints levam tempo para serem ativados. Você pode consultar o status no console do RDS na guia **Conectividade e segurança** do cluster.

Verifique o status do cluster com o AWS CLI comando a seguir.

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

Crie um segredo por meio do 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 appsync-tutorial-rds-secret \
    --secret-string file://creds.json
```

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

## Criar o banco de dados e uma tabela
<a name="creating-db-table"></a>

Primeiro, crie um banco de dados chamado `TESTDB`. No PostgreSQL, um banco de dados é um contêiner que contém tabelas e outros objetos SQL. Valide se o cluster do Aurora Serverless v2 está configurado corretamente antes de adicioná-lo à API do AWS AppSync . Primeiro, crie um banco de dados *TESTDB* com o parâmetro `--sql` da maneira a seguir.

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

 Se isso for executado sem erros, inclua duas tabelas com o comando `create table`:

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

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

Se bem-sucedido, adicione o cluster como uma fonte de dados na API.

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

Agora que a API Data do Aurora Serverless v2 está em execução com tabelas configuradas, criaremos um esquema GraphQL. Você pode criar rapidamente a API importando configurações de tabela a partir de um banco de dados existente usando o assistente de criação de APIs.

Para começar: 

1. No AWS AppSync console, escolha **Create API** e, em seguida, **Start with an Amazon Aurora cluster**. 

1. Especifique os detalhes da API, como **Nome da API**, e selecione o banco de dados para gerar a API.

1. Selecione o banco de dados. Se necessário, atualize a região e selecione o cluster do Aurora e o banco de dados *TESTDB*. 

1. Selecione o segredo e escolha **Importar**. 

1. Depois que as tabelas forem descobertas, atualize os nomes dos tipos. Altere `Todos` para `Todo` e `Tasks` para `Task`. 

1. Visualize o esquema gerado selecionando **Visualizar esquema**. O esquema terá a seguinte aparência: 

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

1. Para a função, você pode AWS 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:ExecuteStatement"
               ],
               "Resource": [
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial",
                   "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial:*"
               ]
           },
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": [
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret",
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret:*"
               ]
           }
       ]
   }
   ```

------

   Observe que há duas declarações nesta política à qual você está concedendo acesso ao perfil. O primeiro recurso é o cluster do Aurora e o segundo é o ARN do AWS Secrets Manager . 

   Selecione **Próximo**, revise os detalhes da configuração e escolha **Criar API**. Agora você tem uma API totalmente operacional. É possível revisar os detalhes completos da API na página **Esquema**. 

## Resolvedores para RDS
<a name="rds-resolvers"></a>

O fluxo de criação da API criou automaticamente os resolvedores para interagir com nossos tipos. Se consultar a página **Esquema**, você encontrará alguns dos resolvedores a seguir.
+ Criar um `todo` por meio do campo `Mutation.createTodo`.
+ Atualizar um `todo` por meio do campo `Mutation.updateTodo`.
+ Excluir um `todo` por meio do campo `Mutation.deleteTodo`.
+ Obter um único `todo` por meio do campo `Query.getTodo`.
+ Listar todos os `todos` por meio do campo `Query.listTodos`.

Você encontrará campos e resolvedores semelhantes anexados para o tipo `Task`. Vamos examinar com mais cuidado alguns dos resolvedores. 

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

No editor de esquemas no AWS AppSync console, no lado direito, escolha `testdb` ao `createTodo(...): Todo` lado de. O código do resolvedor usa a função `insert` do módulo do `rds` para criar dinamicamente uma declaração de inserção que adiciona dados à tabela `todos`. Como estamos trabalhando com o Postgres, podemos aproveitar a declaração `returning` para recuperar os dados inseridos.

Atualize o resolvedor a seguir para especificar devidamente o tipo `DATE` do campo `due`.

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

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

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

Salve o resolvedor. A dica de tipo marca a propriedade `due` no objeto de entrada como um tipo `DATE`. Isso permite que o mecanismo Postgres interprete adequadamente o valor. Depois, atualize o esquema para remover o `id` da entrada `CreateTodo`. Como nosso banco de dados Postgres pode exibir o ID gerado, você pode contar com nele para criar e retornar o resultado como uma única solicitação da maneira a seguir.

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

Faça a alteração e atualize o esquema. Vá até o editor **Consultas** para adicionar um item ao banco de dados da maneira a seguir.

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

Você recebe o resultado a seguir.

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

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

No editor de esquemas no console, à direita, selecione `testdb` ao lado de `listTodos(id: ID!): Todo`. O manipulador de solicitações usa a função de utilitário select para criar uma solicitação dinamicamente em runtime.

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

Queremos filtrar `todos` com base na data `due`. Vamos atualizar o resolvedor no qual converter valores `due` em `DATE`. Atualize a lista de importações e o manipulador de solicitações da maneira a seguir.

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

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

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

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

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

No editor **Consultas**, faça o seguinte.

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

### Mutation.updateTodo
<a name="updatetodo"></a>

Também é possível `update` um `Todo`. No editor de **consultas**, vamos atualizar o primeiro item `Todo` de `id` `1`.

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

Observe que é preciso especificar o `id` do item que você está atualizando. Também é possível determinar uma condição para atualizar somente um item que atenda a condições específicas. Por exemplo, só convém editar o item caso a descrição comece com `edits` da maneira a seguir.

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

Assim como processamos as operações `create` e `list`, podemos atualizar o resolvedor para converter o campo `due` em `DATE`. Salve essas alterações em `updateTodo` da amaneira a seguir.

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

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

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

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

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

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

Agora tente uma atualização com uma condição:

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

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

É possível `delete` um `Todo` com a mutação `deleteTodo`. Isso funciona como a mutação `updateTodo`, e você deve especificar o `id` do item que deseja excluir da maneira a seguir.

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

### Redigir consultas personalizadas
<a name="writing-custom-queries"></a>

Usamos os utilitários do módulo do `rds` para criar declarações SQL. Também podemos redigir a própria declaração estática personalizada para interagir com o banco de dados. Primeiro, atualize o esquema para remover o `id` da entrada `CreateTask`.

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

Depois, crie algumas tarefas. Uma tarefa tem uma relação de chave estrangeira com `Todo` da maneira a seguir.

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

Crie um novo campo no tipo `Query` chamado `getTodoAndTasks` da maneira a seguir.

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

Adicione um campo `tasks` ao tipo `Todo` da maneira a seguir.

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

Salve o esquema. No editor de esquemas no console, à direita, selecione **Anexar resolvedor** para `getTodosAndTasks(id: Int!): Todo`. Selecione a fonte de dados do Amazon RDS. Atualize o resolvedor com o código a seguir.

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

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

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

Nesse código, usamos o modelo de tag `sql` para redigir uma declaração SQL para a qual podemos transmitir com segurança um valor dinâmico em runtime. `createPgStatement` pode receber até duas solicitações SQL por vez. Usamos isso para enviar uma consulta ao `todo` e outra para a `tasks`. É possível fazer isso com uma declaração `JOIN` ou qualquer outro método. A ideia é poder redigir a própria declaração SQL para implementar a lógica de negócios. Para usar a consulta no editor **Consultas**, faça o seguinte.

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

## Excluir o cluster
<a name="rds-delete-cluster"></a>

**Importante**  
A exclusão de um cluster é permanente. Revise o projeto com cuidado antes de realizar essa ação.

Para excluir o cluster:

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