

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Tutorial sui resolver VTL per AWS AppSync
<a name="tutorials"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Le sorgenti di dati e i resolver vengono utilizzati per AWS AppSync tradurre le richieste GraphQL e recuperare informazioni dalle tue risorse. AWS AWS AppSync supporta il provisioning automatico e le connessioni con determinati tipi di fonti di dati. AWS AppSync supporta anche Amazon DynamoDB AWS Lambda, database relazionali (Amazon Aurora Serverless), OpenSearch Amazon Service ed endpoint HTTP come fonti di dati. Puoi utilizzare un'API GraphQL con le tue AWS risorse esistenti o creare sorgenti di dati e resolver partendo da zero. Le sezioni seguenti hanno lo scopo di chiarire alcuni dei casi d'uso più comuni di GraphQL sotto forma di tutorial.

AWS AppSync utilizza modelli di *mappatura scritti in Apache Velocity Template* Language (VTL) per i resolver. [Per ulteriori informazioni sull'utilizzo dei modelli di mappatura, consulta il riferimento ai modelli di mappatura Resolver.](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference) Ulteriori informazioni sull'utilizzo di VTL sono disponibili nella guida alla programmazione dei modelli di mappatura [Resolver](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide).

AWS AppSync supporta il provisioning automatico di tabelle DynamoDB da uno schema GraphQL come descritto in Provision from schema (opzionale) e Launch a sample schema. Puoi anche importare i dati da una tabella DynamoDB esistente che creerà lo schema e connetterà i resolver. Questo è descritto in Importazione da Amazon DynamoDB (opzionale).

**Topics**
+ [Creazione di una semplice applicazione post utilizzando i resolver DynamoDB](tutorial-dynamodb-resolvers.md)
+ [AWS Lambda Usare i resolver](tutorial-lambda-resolvers.md)
+ [Utilizzo dei resolver di servizio OpenSearch](tutorial-elasticsearch-resolvers.md)
+ [Utilizzo di resolver locali](tutorial-local-resolvers.md)
+ [Combinazione di resolver GraphQL](tutorial-combining-graphql-resolvers.md)
+ [Utilizzo delle operazioni batch di DynamoDB](tutorial-dynamodb-batch.md)
+ [Esecuzione di transazioni DynamoDB](tutorial-dynamodb-transact.md)
+ [Utilizzo di resolver HTTP](tutorial-http-resolvers.md)
+ [Utilizzo dei resolver Aurora Serverless v2](tutorial-rds-resolvers.md)
+ [Utilizzo dei resolver per pipeline](tutorial-pipeline-resolvers.md)
+ [Utilizzo delle operazioni Delta Sync su fonti di dati con versioni](tutorial-delta-sync.md)

# Creazione di una semplice applicazione post utilizzando i resolver DynamoDB
<a name="tutorial-dynamodb-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Questo tutorial mostra come importare tabelle Amazon DynamoDB personalizzate e collegarle AWS AppSync a un'API GraphQL.

Puoi consentire il AWS AppSync provisioning delle risorse DynamoDB per tuo conto. In alternativa, se preferisci, puoi connettere le tabelle esistenti a uno schema GraphQL creando un'origine dati e un resolver. In entrambi i casi, potrai leggere e scrivere nel database DynamoDB tramite istruzioni GraphQL e sottoscrivere dati in tempo reale.

Dovrai eseguire operazioni specifiche perché le istruzioni GraphQL possano essere tradotte in operazioni di DynamoDB e perché le risposte possano essere ritradotte in GraphQL. Questo tutorial descrive il processo di configurazione attraverso diversi scenari e modelli di accesso ai dati reali.

## Configurazione delle tabelle DynamoDB
<a name="setting-up-your-ddb-tables"></a>

Per iniziare questo tutorial, devi prima seguire i passaggi seguenti per fornire AWS le risorse.

1. Esegui il provisioning AWS delle risorse utilizzando il seguente AWS CloudFormation modello nella CLI:

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

   In alternativa, puoi avviare il seguente CloudFormation stack nella regione US-West 2 (Oregon) del tuo account. AWS 

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

   Ciò crea quanto segue:
   + Una tabella DynamoDB `AppSyncTutorial-Post` chiamata che conterrà i dati. `Post`
   + Un ruolo IAM e una policy gestita da IAM associata AWS AppSync per consentire l'interazione con la `Post` tabella.

1. Per ulteriori informazioni sullo stack e sulle risorse create, esegui il comando dell'interfaccia a riga di comando seguente:

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

1. Per eliminare le risorse in un secondo momento, puoi eseguire quanto segue:

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

## Creazione dell'API GraphQL
<a name="creating-your-graphql-api"></a>

Per creare l'API GraphQL in: AWS AppSync

1. Accedi a Console di gestione AWS e apri la [AppSync console](https://console.aws.amazon.com/appsync/).

   1. Nella **APIs dashboard**, scegli **Crea API**.

1. Nella finestra **Personalizza la tua API o importa da Amazon DynamoDB**, **scegli** Crea da zero.

   1. Scegli **Start** a destra della stessa finestra.

1. Nel campo **Nome API**, imposta il nome dell'API su`AWSAppSyncTutorial`.

1. Scegli **Create** (Crea).

La AWS AppSync console crea una nuova API GraphQL utilizzando la modalità di autenticazione della chiave API. Puoi usare la console per configurare ulteriormente l'API GraphQL ed eseguire query sull'API per le parti restanti di questo tutorial.

## Definizione di una post API di base
<a name="defining-a-basic-post-api"></a>

Ora che hai creato un'API AWS AppSync GraphQL, puoi configurare uno schema di base che consente la creazione, il recupero e l'eliminazione di base dei dati post.

1. [Accedi a Console di gestione AWS e apri la console. AppSync ](https://console.aws.amazon.com/appsync/)

   1. Nella **APIs dashboard**, scegli l'API che hai appena creato.

1. Nella **barra laterale**, scegli **Schema**.

   1. Nel riquadro **Schema**, sostituisci il contenuto con il seguente codice:

     ```
     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. Scegli **Save** (Salva).

Questo schema definisce un tipo `Post` e le operazioni per aggiungere e ottenere oggetti `Post`.

## Configurazione dell'origine dati per le tabelle DynamoDB
<a name="configuring-the-data-source-for-the-ddb-tables"></a>

Successivamente, collega le query e le mutazioni definite nello schema alla tabella DynamoDB`AppSyncTutorial-Post`.

Innanzitutto, è AWS AppSync necessario conoscere le proprie tabelle. A questo scopo, devi configurare un'origine dati in AWS AppSync:

1. Accedi a Console di gestione AWS e apri la [AppSync console](https://console.aws.amazon.com/appsync/).

   1. Nella **APIs dashboard**, scegli la tua API GraphQL.

   1. Nella **barra laterale**, scegli **Data** Sources.

1. Seleziona **Create data source (Crea origine dati)**.

   1. Per il **nome dell'origine dati**, inserisci. `PostDynamoDBTable` 

   1. Per il **tipo di origine dati**, scegli la tabella **Amazon DynamoDB**.

   1. **Per **Regione**, scegli US-WEST-2.**

   1. Per **Nome tabella**, scegli la tabella **AppSyncTutorial-Post** DynamoDB.

   1. Crea un nuovo ruolo IAM (consigliato) o scegli un ruolo esistente con l'`lambda:invokeFunction`autorizzazione IAM. I ruoli esistenti richiedono una policy di fiducia, come spiegato nella sezione [Allegare una fonte di dati](attaching-a-data-source.md). 

      Di seguito è riportato un esempio di policy IAM che dispone delle autorizzazioni necessarie per eseguire operazioni sulla risorsa:

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

****  

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

------

1. Scegli **Create** (Crea).

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

**Dopo aver AWS AppSync acquisito conoscenza della tabella DynamoDB, è possibile collegarla a singole query e mutazioni definendo i Resolver.** Il primo resolver creato è il `addPost` resolver, che consente di creare un post nella tabella DynamoDB. `AppSyncTutorial-Post`

Un resolver include i componenti seguenti:
+ Posizione nello schema GraphQL per collegare il resolver. In questo caso, stai configurando un resolver nel campo `addPost` nel tipo `Mutation`. Questo resolver verrà richiamato quando il chiamante chiama `mutation { addPost(...){...} }`.
+ Origine dati da usare per il resolver. In questo caso, userai l'origine dati `PostDynamoDBTable` che hai definito in precedenza, per poter aggiungere voci nella tabella DynamoDB `AppSyncTutorial-Post`.
+ Modello di mappatura della richiesta. Lo scopo del modello di mappatura delle richieste è prendere la richiesta in arrivo dal chiamante e tradurla in istruzioni da eseguire con AWS AppSync DynamoDB.
+ Modello di mappatura della risposta. Lo scopo del modello di mappatura della risposta è ritradurre la risposta proveniente da DynamoDB nel risultato previsto da GraphQL. Questo è utile se la forma dei dati in DynamoDB è diversa rispetto al tipo `Post` in GraphQL, ma in questo caso hanno la stessa forma e di conseguenza puoi semplicemente passare i dati.

Per configurare il resolver:

1. [Accedi a e apri la console. Console di gestione AWS AppSync ](https://console.aws.amazon.com/appsync/)

   1. Nella **APIs dashboard**, scegli la tua API GraphQL.

   1. Nella **barra laterale**, scegli **Data** Sources.

1. Seleziona **Create data source (Crea origine dati)**.

   1. Per il **nome dell'origine dati**, inserisci. `PostDynamoDBTable` 

   1. Per il **tipo di origine dati**, scegli la tabella **Amazon DynamoDB**.

   1. **Per **Regione**, scegli US-WEST-2.**

   1. Per **Nome tabella**, scegli la tabella **AppSyncTutorial-Post** DynamoDB.

   1. Crea un nuovo ruolo IAM (consigliato) o scegli un ruolo esistente con l'`lambda:invokeFunction`autorizzazione IAM. I ruoli esistenti richiedono una policy di fiducia, come spiegato nella sezione [Allegare una fonte di dati](attaching-a-data-source.md). 

      Di seguito è riportato un esempio di policy IAM che dispone delle autorizzazioni necessarie per eseguire operazioni sulla risorsa:

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

****  

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

------

1. Scegli **Create** (Crea).

1. Scegli la scheda **Schema**.

1. Nel riquadro **Data types (Tipi di dati** a destra, individua il campo **addPost** sul tipo **Mutation (Mutazione)**, quindi scegli **Attach (Collega)**.

1. Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (solo VTL**).

1. Nel **nome origine dati**, scegliere **PostDynamoDBTable**.

1. Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota:** un *tipo* è specificato in tutte le chiavi e in tutti i valori di attributo. Ad esempio, puoi impostare il campo `author` su `{ "S" : "${context.arguments.author}" }`. La `S` parte indica a AWS AppSync e DynamoDB che il valore sarà un valore stringa. Il valore effettivo viene popolato dall'argomento `author`. Analogamente, il campo `version` è un campo numerico, perché usa `N` per il tipo. Infine, stai anche inizializzando i campi `ups`, `downs` e `version`.

   Per questo tutorial avete specificato che il `ID!` tipo GraphQL, che indicizza il nuovo elemento inserito in DynamoDB, fa parte degli argomenti del client. AWS AppSync viene fornito con un'utilità per la generazione automatica di ID chiamata `$utils.autoId()` che avresti potuto utilizzare anche sotto forma di. `"id" : { "S" : "${$utils.autoId()}" }` Puoi quindi lasciare semplicemente `id: ID!` fuori dalla definizione dello schema `addPost()` e questo verrà inserito automaticamente. Non utilizzerai questa tecnica per questo tutorial, ma dovresti considerarla una buona pratica quando scrivi su tabelle DynamoDB.

   Per ulteriori informazioni sui modelli di mappatura, consulta la documentazione di riferimento [Panoramica sui modelli di mappatura dei resolver](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview). Per ulteriori informazioni sulla mappatura delle GetItem richieste, consulta la [GetItem](aws-appsync-resolver-mapping-template-reference-dynamodb-getitem.md)documentazione di riferimento. Per ulteriori informazioni sui tipi, consulta la documentazione di riferimento [Sistema di tipi (mappatura della richiesta)](aws-appsync-resolver-mapping-template-reference-dynamodb-typed-values-request.md).

1. Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

    **Nota**: poiché la forma dei dati nella tabella `AppSyncTutorial-Post` corrisponde esattamente alla forma del tipo `Post` in GraphQL, il modello di mappatura della risposta si limita a passare i risultati direttamente. Inoltre, tutti gli esempi mostrati in questo tutorial usano lo stesso modello di mappatura della risposta e di conseguenza devi creare solo un file.

1. Scegli **Save** (Salva).

### Chiamata dell'API per aggiungere un post
<a name="call-the-api-to-add-a-post"></a>

Ora che il resolver è configurato, AWS AppSync puoi tradurre una `addPost` mutazione in entrata in un'operazione DynamoDB. PutItem Puoi ora eseguire una mutazione per inserire contenuto nella tabella.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**.

  ```
  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
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ I risultati del nuovo post creato verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Ecco che cosa è successo:
+ AWS AppSync `addPost`ha ricevuto una richiesta di mutazione.
+ AWS AppSync ha preso la richiesta e il modello di mappatura della richiesta e ha generato un documento di mappatura della richiesta. L'aspetto sarà simile al seguente:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "PutItem",
      "key" : {
          "id" : { "S" : "123" }
      },
      "attributeValues" : {
          "author": { "S" : "AUTHORNAME" },
          "title": { "S" : "Our first post!" },
          "content": { "S" : "This is our first post." },
          "url": { "S" : "https://aws.amazon.com/appsync/" },
          "ups" : { "N" : 1 },
          "downs" : { "N" : 0 },
          "version" : { "N" : 1 }
      }
  }
  ```
+ AWS AppSync ha utilizzato il documento di mappatura della richiesta per generare ed eseguire una richiesta DynamoDB`PutItem`.
+ AWS AppSync ha preso i risultati della `PutItem` richiesta e li ha riconvertiti in tipi 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
  }
  ```
+ Ha passato il documento di mappatura della risposta, che è stato passato invariato.
+ Ha restituito il nuovo oggetto creato nella risposta GraphQL.

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

Ora che sei in grado di aggiungere dati alla tabella `AppSyncTutorial-Post` DynamoDB, devi configurare `getPost` la query in modo che possa recuperare i dati dalla tabella. `AppSyncTutorial-Post` A questo scopo, devi configurare un altro resolver.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo **getPost** sul tipo **Query**, quindi scegliere **Attach (Collega)**.
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver** (solo VTL).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
      }
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).

### Chiamata dell'API per ottenere un post
<a name="call-the-api-to-get-a-post"></a>

Ora il resolver è stato configurato, AWS AppSync sa come tradurre una `getPost` query in entrata in un'operazione DynamoDB. `GetItem` Puoi ora eseguire una query per recuperare il post creato prima.
+ Seleziona la scheda **Queries (Query)**.
+ Nel riquadro **Queries (Query)** incollare quanto segue:

  ```
  query getPost {
    getPost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post recuperato da DynamoDB dovrebbe apparire nel riquadro dei risultati a destra del riquadro delle query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Ecco che cosa è successo:
+ AWS AppSync ha ricevuto una `getPost` richiesta di interrogazione.
+ AWS AppSync ha preso la richiesta e il modello di mappatura della richiesta e ha generato un documento di mappatura della richiesta. L'aspetto sarà simile al seguente:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : { "S" : "123" }
      }
  }
  ```
+ AWS AppSync ha utilizzato il documento di mappatura della richiesta per generare ed eseguire una richiesta DynamoDB GetItem .
+ AWS AppSync ha preso i risultati della `GetItem` richiesta e li ha riconvertiti in tipi 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
  }
  ```
+ Ha passato il documento di mappatura della risposta, che è stato passato invariato.
+ Ha restituito l'oggetto recuperato nella risposta.

In alternativa, prendiamo il seguente esempio:

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

Se la tua `getPost` query richiede solo il`id`, e `author``title`, puoi modificare il modello di mappatura della richiesta per utilizzare le espressioni di proiezione per specificare solo gli attributi che desideri dalla tabella DynamoDB per evitare trasferimenti di dati non necessari da DynamoDB a. AWS AppSync Ad esempio, il modello di mappatura della richiesta può essere simile allo snippet riportato di seguito:

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

## Creare una mutazione UpdatePost (DynamoDB) UpdateItem
<a name="create-an-updatepost-mutation-ddb-updateitem"></a>

Finora è possibile creare e recuperare `Post` oggetti in DynamoDB. Ora configurerai una nuova mutazione per poter aggiornare gli oggetti. Questa operazione verrà eseguita utilizzando l'operazione UpdateItem DynamoDB.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modificare il tipo `Mutation` per aggiungere una nuova mutazione `updatePost` come segue:

  ```
  type Mutation {
      updatePost(
          id: ID!,
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post
      addPost(
          author: String!
          title: String!
          content: String!
          url: String!
      ): Post!
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **updatePost** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (**solo VTL).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota:** questo resolver utilizza DynamoDB UpdateItem, che è significativamente diverso dall'operazione. PutItem Invece di scrivere l'intero elemento, stai semplicemente chiedendo a DynamoDB di aggiornare determinati attributi. Questa operazione viene eseguita utilizzando DynamoDB Update Expressions. L'espressione stessa viene specificata nel campo `expression` nella sezione `update`. L'espressione indica di impostare gli attributi `author`, `title`, `content` e url e quindi di incrementare il campo `version`. I valori da usare non appaiono nell'espressione stessa. L'espressione include segnaposto che indicano nomi che iniziano con un carattere di due punti e che vengono quindi definiti nel campo `expressionValues`. Infine, DynamoDB contiene parole riservate che non possono apparire in. `expression` Ad esempio, poiché `url` è una parola riservata, per aggiornare il campo `url` puoi usare i segnaposto dei nomi e definirli nel campo `expressionNames`.

  Per ulteriori informazioni sulla mappatura delle `UpdateItem` richieste, consulta la [UpdateItem](aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem.md)documentazione di riferimento. Per ulteriori informazioni su come scrivere espressioni di aggiornamento, consulta la documentazione di [ UpdateExpressions DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

### Chiamata dell'API per aggiornare un post
<a name="call-the-api-to-update-a-post"></a>

Ora che il resolver è stato configurato, AWS AppSync sa come tradurre una `update` mutazione in entrata in un'operazione DynamoDB. `Update` Possiamo ora eseguire una mutazione per aggiornare l'item scritto in precedenza.
+ Seleziona la scheda **Queries (Query)**.
+ Nel riquadro **Queries (Query)** incollare la seguente mutazione. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation updatePost {
    updatePost(
      id:"123"
      author: "A new author"
      title: "An updated author!"
      content: "Now with updated content!"
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post aggiornato in DynamoDB dovrebbe apparire nel riquadro dei risultati a destra del riquadro delle query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

In questo esempio, i `downs` campi `ups` and non sono stati modificati perché il modello di mappatura delle richieste non AWS AppSync chiedeva a DynamoDB di fare nulla con quei campi. Inoltre, il `version` campo è stato incrementato di 1 perché hai chiesto AWS AppSync a DynamoDB di aggiungere 1 al campo. `version`

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

Questo è un buon punto di partenza per la mutazione `updatePost`, ma presenta due problemi principali:
+ Se vogliamo aggiornare un solo campo, dovremo aggiornare tutti i campi.
+ Se due persone stanno modificando l'oggetto, rischiamo di perdere informazioni.

Per risolvere questi problemi, modificherai la mutazione `updatePost` in modo da modificare solo gli argomenti che sono stati specificati nella richiesta e quindi aggiungerai una condizione all'operazione `UpdateItem`.

1. Scegli la scheda **Schema**.

1. Nel riquadro **Schema**, modifica il `updatePost` campo nel `Mutation` tipo per rimuovere i punti esclamativi dagli `url` argomenti`author`, `title``content`, e assicurandoti di lasciare il campo così com'è. `id` In questo modo, questo argomento verrà reso facoltativo. Aggiungere inoltre un nuovo argomento `expectedVersion` obbligatorio.

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

1. Scegli **Save** (Salva).

1. Nel riquadro **Data types (Tipi di dati)** alla destra, individuare il campo **updatePost** sul tipo **Mutation (Mutazione)**.

1. Scegli **PostDynamoDBTable**di aprire il resolver esistente.

1. Modifica il modello di mappatura della richiesta nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

1. Scegli **Save** (Salva).

Questo modello è uno degli esempi più complessi. Dimostra la potenza e la flessibilità dei modelli di mappatura. Passa su tutti gli argomenti, ignorando `id` e `expectedVersion`. Se l'argomento è impostato su qualcosa, chiede AWS AppSync a DynamoDB di aggiornare quell'attributo sull'oggetto in DynamoDB. Se l'attributo è impostato su null, chiede AWS AppSync a DynamoDB di rimuovere quell'attributo dall'oggetto post. Se non è stato specificato un argomento, lo ignorerà. Il modello incrementa anche il campo `version`.

È presente anche una nuova sezione `condition`. Un'espressione di condizione consente di indicare AWS AppSync a DynamoDB se la richiesta deve avere successo o meno in base allo stato dell'oggetto già in DynamoDB prima dell'esecuzione dell'operazione. In questo caso, si desidera che la `UpdateItem` richiesta abbia esito positivo solo se il `version` campo dell'elemento attualmente in DynamoDB corrisponde esattamente all'argomento. `expectedVersion`

Per ulteriori informazioni sulle espressioni di condizione, consulta la documentazione di riferimento delle [espressioni di condizione](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md).

### Chiamata dell'API per aggiornare un post
<a name="id1"></a>

Proviamo ad aggiornare l'oggetto `Post` con il nuovo resolver:
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation updatePost {
    updatePost(
      id:123
      title: "An empty story"
      content: null
      expectedVersion: 2
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post aggiornato in DynamoDB dovrebbe apparire nel riquadro dei risultati a destra del riquadro delle query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

In questa richiesta, hai chiesto a DynamoDB di aggiornare solo `title` il `content` campo AWS AppSync and. Tutti gli altri campi sono stati ignorati (con l'eccezione dell'aumento del campo `version`). Hai impostato l'attributo `title` su un nuovo valore e abbiamo rimosso l'attributo `content` dal post. I campi `author`, `url`, `ups` e `downs` sono stati lasciati invariati.

Prova a eseguire di nuovo la richiesta di mutazione, lasciandola esattamente così com'è. Noterai una risposta simile alla seguente:

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

La richiesta non riesce perché l'espressione di condizione restituisce false:
+ La prima volta che hai eseguito la richiesta, il valore del `version` campo del post in DynamoDB `2` era corrispondente all'argomento. `expectedVersion` La richiesta è riuscita, il che significa che il `version` campo è stato incrementato in DynamoDB a. `3`
+ La seconda volta che hai eseguito la richiesta, il valore del `version` campo del post in DynamoDB `3` era, che non corrispondeva all'argomento. `expectedVersion`

Questo modello viene in genere chiamato *blocco ottimistico*.

Una caratteristica di un resolver AWS AppSync DynamoDB è che restituisce il valore corrente dell'oggetto post in DynamoDB. Puoi trovare questo valore nel campo `data` nella sezione `errors` della risposta GraphQL. L'applicazione può usare queste informazioni per decidere come procedere. In questo caso, puoi vedere che il `version` campo dell'oggetto in DynamoDB è impostato su, quindi puoi semplicemente aggiornare `expectedVersion` l'argomento `3` `3` a e la richiesta avrà nuovamente esito positivo.

Per ulteriori informazioni sulla gestione degli errori del controllo delle condizioni, consulta la documentazione di riferimento dei modelli di mappatura delle [espressioni di condizione](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md).

## Crea mutazioni UpvotePost e DownvotePost (DynamoDB) UpdateItem
<a name="create-upvotepost-and-downvotepost-mutations-ddb-updateitem"></a>

Il tipo `Post` include i campi `ups` e `downs` per consentire di registrare voti positivi e voti negativi, ma finora l'API non ci ha permesso di eseguire alcuna operazione con questi campi. Aggiungiamo ora alcune mutazioni per poter assegnare voti positivi e negativi ai post.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modifica il tipo per aggiungere nuove e mutazioni come segue`Mutation`: `upvotePost` `downvotePost`

  ```
  type Mutation {
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **upvotePost** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (solo VTL**).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD ups :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo `downvotePost` appena creato sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD downs :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).

### Chiamata dell'API per assegnare voti positivi e voti negativi a un post
<a name="call-the-api-to-upvote-and-downvote-a-post"></a>

Ora i nuovi resolver sono stati configurati, AWS AppSync sa come tradurre un'operazione in entrata o una `upvotePost` mutazione `downvote` in DynamoDB. UpdateItem Ora puoi eseguire mutazioni per assegnare voti positivi e voti negativi al post creato prima.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation votePost {
    upvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post viene aggiornato in DynamoDB e dovrebbe apparire nel riquadro dei risultati a destra del riquadro delle query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

  ```
  {
    "data": {
      "upvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 0,
        "version": 4
      }
    }
  }
  ```
+ Scegli **Execute query (Esegui query)** più volte. I campi `ups` e `version` vengono incrementati di 1 ogni volta che esegui la query.
+ Modifica la query in modo da chiamare la mutazione `downvotePost` come segue:

  ```
  mutation votePost {
    downvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione). Questa volta, i campi `downs` e `version` vengono incrementati di 1 ogni volta che esegui la query.

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

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

La prossima mutazione che vuoi configurare è quella per l'eliminazione di un post. Questa operazione verrà eseguita utilizzando l'operazione `DeleteItem` DynamoDB.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modificare il tipo `Mutation` per aggiungere una nuova mutazione `deletePost` come segue:

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

  A questo punto il campo `expectedVersion` viene reso opzionale, operazione spiegata in seguito al momento di aggiungere il modello di mappatura della richiesta.
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **delete (elimina)** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (**solo VTL).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota**: l'argomento `expectedVersion` è un argomento facoltativo. Se il chiamante imposta un `expectedVersion` argomento nella richiesta, il modello aggiunge una condizione che consente l'esito positivo della `DeleteItem` richiesta solo se l'elemento è già stato eliminato o se l'`version`attributo del post in DynamoDB corrisponde esattamente a. `expectedVersion` Se non viene inserito, nessuna espressione di condizione viene specificata nella richiesta `DeleteItem`. Ha successo indipendentemente dal valore o dal fatto che l'elemento esista o meno in DynamoDB. `version`
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota**: anche se stai eliminando un item, puoi restituire l'item che è stato eliminato, se non è già stato eliminato.
+ Scegli **Save** (Salva).

Per ulteriori informazioni sulla mappatura delle `DeleteItem` richieste, consulta la documentazione di riferimento. [DeleteItem](aws-appsync-resolver-mapping-template-reference-dynamodb-deleteitem.md)

### Chiamata dell'API per eliminare un post
<a name="call-the-api-to-delete-a-post"></a>

Ora che il resolver è stato configurato, AWS AppSync sa come tradurre una `delete` mutazione in entrata in un'operazione DynamoDB. `DeleteItem` Puoi ora eseguire una mutazione per eliminare qualcosa nella tabella.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation deletePost {
    deletePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post viene eliminato da DynamoDB. Nota che AWS AppSync restituisce il valore dell'elemento che è stato eliminato da DynamoDB, che dovrebbe apparire nel riquadro dei risultati a destra del riquadro delle query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Il valore viene restituito solo se la chiamata a `deletePost` è quella che lo ha effettivamente eliminato da DynamoDB.
+ Scegliere nuovamente **Execute query (Esegui query)**.
+ La chiamata riesce ancora, ma non viene restituito alcun valore.

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

Proviamo ora a eliminare un post, questa volta specificando `expectedValue`. In primo luogo, tuttavia, dovrai creare un nuovo post perché hai appena eliminato quello usato fino a questo punto.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**.

  ```
  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
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ I risultati del nuovo post creato verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. Annotare il valore di `id` del nuovo oggetto creato, perché sarà necessario tra poco. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Proviamo ora a eliminare il post, ma inserendo un valore errato per `expectedVersion`:
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 9999
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).

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

  La richiesta non è riuscita perché l'espressione della condizione risulta falsa: il valore `version` del post in DynamoDB non corrisponde a quello specificato `expectedValue` negli argomenti. Il valore corrente dell'oggetto viene restituito nel campo `data` nella sezione `errors` della risposta GraphQL.
+ Riprova la richiesta, correggendo il valore di `expectedVersion`:

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 1
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Questa volta la richiesta ha esito positivo e viene restituito il valore che è stato eliminato da DynamoDB:

  ```
  {
    "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
      }
    }
  }
  ```
+ Scegliere nuovamente **Execute query (Esegui query)**.
+ La chiamata riesce ancora, ma questa volta non viene restituito alcun valore perché il post è già stato eliminato in DynamoDB.

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

## Configurazione del resolver Up the allPost (scansione di DynamoDB)
<a name="setting-up-the-allpost-resolver-dynamodb-scan"></a>

Fino a questo punto l'API è utile solo se conosci l'`id` di ogni post che vuoi guardare. Aggiungiamo ora un nuovo resolver che restituisce tutti i post nella tabella.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modificare il tipo `Query` per aggiungere una nuova query `allPost` come segue:

  ```
  type Query {
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```
+ Aggiungi un nuovo tipo `PaginationPosts`:

  ```
  type PaginatedPosts {
      posts: [Post!]!
      nextToken: String
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **allPost** sul tipo **Query**, quindi scegliere **Attach (Collega)**.
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver** (solo VTL).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

  Questo resolver include due argomenti facoltativi: `count`, che specifica il numero massimo di item da restituire in una singola chiamata, e `nextToken`, che può essere usato per recuperare il set successivo di risultati (vedrai da dove proviene il valore per `nextToken` più tardi).
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota**: questo modello di mappatura della risposta è diverso da tutti gli altri descritti finora. Il risultato della query `allPost` è un oggetto `PaginatedPosts`, che contiene un elenco dei post e un token di paginazione. La forma di questo oggetto è diversa da quella restituita dal Resolver AWS AppSync DynamoDB: l'elenco dei post viene richiamato `items` nei risultati del Resolver AWS AppSync DynamoDB, ma viene richiamato. `posts` `PaginatedPosts`
+ Scegli **Save** (Salva).

Per ulteriori informazioni sulla mappatura della richiesta `Scan`, consulta la documentazione di riferimento di [Scan](aws-appsync-resolver-mapping-template-reference-dynamodb-scan.md).

### Chiamata dell'API per analizzare tutti i post
<a name="call-the-api-to-scan-all-posts"></a>

Ora il resolver è stato configurato, AWS AppSync sa come tradurre una `allPost` query in entrata in un'operazione DynamoDB. `Scan` Puoi ora analizzare la tabella per recuperare tutti i post.

Prima di provare, devi immettere nella tabella alcuni dati, perché hai eliminato tutti quelli usati finora.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**.

  ```
  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 }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).

Analizziamo ora la tabella, restituendo cinque risultati per volta.
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  query allPost {
    allPost(count: 5) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ I primi cinque post appariranno nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Hai ottenuto cinque risultati e una `nextToken` che è possibile utilizzare per ottenere la prossima serie di risultati.
+ Aggiornare la query `allPost` in modo da includere `nextToken` dal set precedente di risultati:

  ```
  query allPost {
    allPost(
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0="
    ) {
      posts {
        id
        author
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ I quattro post rimanenti appariranno nel riquadro dei risultati a destra del riquadro della query. Questo set di risultati non contiene `nextToken`, perché hai analizzato tutti i nove post, senza che ne sia rimasto alcuno. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

## Configurazione dell' allPostsByAuthor Resolver (DynamoDB Query)
<a name="setting-up-the-allpostsbyauthor-resolver-ddb-query"></a>

Oltre a scansionare DynamoDB per tutti i post, puoi anche interrogare DynamoDB per recuperare i post creati da un autore specifico. La tabella DynamoDB che hai creato in precedenza contiene già `GlobalSecondaryIndex` una `author-index` chiamata che puoi utilizzare con un'operazione DynamoDB per recuperare tutti i `Query` post creati da un autore specifico.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modificare il tipo `Query` per aggiungere una nuova query `allPostsByAuthor` come segue:

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

   **Nota:** usa lo stesso tipo `PaginatedPosts` utilizzato con la query `allPost`.
+ Scegli **Save** (Salva).
+ **Nel riquadro **Tipi di dati** a destra, individua il campo **allPostsByAutore** appena creato nel campo Tipo di **query**, quindi scegli Allega.**
+ Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (solo VTL**).
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

  Come per il resolver `allPost`, questo resolver include due argomenti facoltativi: `count`, che specifica il numero massimo di item da restituire in una singola chiamata, e `nextToken`, che può essere usato per recuperare il set successivo di risultati (il valore per `nextToken` può essere ottenuto da una chiamata precedente).
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

   **Nota**: questo è lo stesso modello di mappatura della risposta usato nel resolver `allPost`.
+ Scegli **Save** (Salva).

Per ulteriori informazioni sulla mappatura della richiesta `Query`, consulta la documentazione di riferimento di [Query](aws-appsync-resolver-mapping-template-reference-dynamodb-query.md).

### Chiamata dell'API per eseguire una query per recuperare tutti i post di un autore
<a name="call-the-api-to-query-all-posts-by-an-author"></a>

Ora che il resolver è stato configurato, AWS AppSync sa come tradurre una `allPostsByAuthor` mutazione in entrata in un'operazione DynamoDB rispetto all'indice. `Query` `author-index` Puoi ora eseguire una query sulla tabella per recuperare tutti i post di un autore specifico.

Prima di continuare, tuttavia, devi immettere altri post nella tabella, perché i post presenti fino a questo punto hanno lo stesso autore.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**.

  ```
  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 }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).

Esegui ora la query sulla tabella, per restituire tutti i post il cui autore è `Nadia`.
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(author: "Nadia") {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Tutti i post il cui autore è `Nadia` verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

La paginazione funziona per `Query` esattamente come per `Scan`. Ad esempio, cerchiamo tutti i post di `AUTHORNAME`, restituendone cinque per volta.
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Tutti i post il cui autore è `AUTHORNAME` verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
      }
    }
  }
  ```
+ Aggiornare l'argomento `nextToken` con il valore restituito dalla query precedente, come segue:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Tutti i post rimanenti il cui autore è `AUTHORNAME` verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

  ```
  {
    "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 di set
<a name="using-sets"></a>

Fino a questo punto il `Post` tipo era un oggetto piatto. key/value È inoltre possibile modellare oggetti complessi con il resolver AWS AppSyncDynamo DB, come set, elenchi e mappe.

Aggiorniamo ora il tipo `Post` perché includa tag. Un post può avere 0 o più tag, che vengono memorizzati in DynamoDB come set di stringhe. Dobbiamo anche configurare alcune mutazioni per aggiungere e rimuovere tag e una query per analizzare i post con un tag specifico.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, modificare il tipo `Post` per aggiungere un nuovo campo `tags` come segue:

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
  }
  ```
+ Nel riquadro **Schema**, modificare il tipo `Query` per aggiungere una nuova query `allPostsByTag` come segue:

  ```
  type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID): Post
  }
  ```
+ Nel riquadro **Schema**, modifica il `Mutation` tipo per aggiungere nuove `addTag` e `removeTag` mutazioni come segue:

  ```
  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!
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Tipi di dati** a destra, trova il campo **allPostsByTag** appena creato nel tipo di **query**, quindi scegli **Allega**.
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan",
      "filter": {
        "expression": "contains (tags, :tag)",
          "expressionValues": {
            ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **addTag** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD tags :tags, version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **removeTag** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "DELETE tags :tags ADD version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).

### Chiamata dell'API per usare tag
<a name="call-the-api-to-work-with-tags"></a>

Ora che hai configurato i resolver, AWS AppSync sa come tradurre le richieste in entrata `addTag` e le richieste `allPostsByTag` in DynamoDB e nelle operazioni. `removeTag` `UpdateItem` `Scan`

Per provare, selezioniamo uno dei post creati prima. Ad esempio, è possibile utilizzare un post redatto dall'utente `Nadia`.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "Nadia"
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Tutti i post di Nadia verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you known...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```
+ Utilizza quella con il titolo `"The cutest dog in the world"`. Annotiamo il valore di `id`, perché dovrà essere usato più tardi.

Proviamo ora ad aggiungere un tag `dog`.
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation addTag {
    addTag(id:10 tag: "dog") {
      id
      title
      tags
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post viene aggiornato con il nuovo tag.

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

Possiamo anche aggiungere più tag come segue:
+ Aggiornare la mutazione in modo da modificare l'argomento `tag` in `puppy`.

  ```
  mutation addTag {
    addTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post viene aggiornato con il nuovo tag.

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

Puoi anche eliminare tag:
+ Incollare la mutazione seguente nel riquadro **Queries (Query)**. Dovremo anche aggiornare l'argomento `id` sul valore che abbiamo annotato in precedenza.

  ```
  mutation removeTag {
    removeTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Il post viene aggiornato e il tag `puppy` viene eliminato.

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

Puoi anche cercare tutti i post che includono un tag:
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  query allPostsByTag {
    allPostsByTag(tag: "dog") {
      posts {
        id
        title
        tags
      }
      nextToken
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Vengono restituiti tutti i post con il tag `dog` come segue:

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

## Uso di elenchi e mappe
<a name="using-lists-and-maps"></a>

Oltre a utilizzare i set DynamoDB, puoi anche utilizzare elenchi e mappe DynamoDB per modellare dati complessi in un singolo oggetto.

Valutiamo ora la possibilità di aggiungere commenti ai post. Questo verrà modellato come un elenco di oggetti della mappa sull'`Post`oggetto in DynamoDB.

 **Nota:** in un'applicazione reale, i commenti sarebbero modellati nella propria tabella. Per questo tutorial, aggiungerli nella tabella `Post`.
+ Scegli la scheda **Schema**.
+ Nel riquadro **Schema**, aggiungere un nuovo tipo `Comment` come segue:

  ```
  type Comment {
      author: String!
      comment: String!
  }
  ```
+ Nel riquadro **Schema**, modificare il tipo `Post` per aggiungere un nuovo campo `comments` come segue:

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
  }
  ```
+ Nel riquadro **Schema**, modificare il tipo `Mutation` per aggiungere una nuova mutazione `addComment` come segue:

  ```
  type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ Scegli **Save** (Salva).
+ Nel riquadro **Data types (Tipi di dati)** a destra, individuare il campo appena creato **addComment** sul tipo **Mutation (Mutazione)**, quindi scegliere **Attach (Collega)**.
+ Nel **nome origine dati**, scegliere **PostDynamoDBTable**.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

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

  Questa espressione di aggiornamento aggiunge un elenco contenente il nuovo commento all'elenco `comments` esistente. Se l'elenco non esiste già, viene creato.
+ Incollare il codice seguente nella sezione **Configure the request mapping template (Configura il modello di mappatura della richiesta)**:

  ```
  $utils.toJson($context.result)
  ```
+ Scegli **Save** (Salva).

### Chiamata dell'API per aggiungere un commento
<a name="call-the-api-to-add-a-comment"></a>

Ora che hai configurato i resolver, AWS AppSync sa come tradurre le richieste `addComment` in arrivo in operazioni DynamoDB. `UpdateItem`

Proviamo ora ad aggiungere un commento allo stesso post cui abbiamo aggiunto i tag.
+ Seleziona la scheda **Queries (Query)**.
+ Incollare la query seguente nel riquadro **Queries (Query)**:

  ```
  mutation addComment {
    addComment(
      id:10
      author: "Steve"
      comment: "Such a cute dog."
    ) {
      id
      comments {
        author
        comment
      }
    }
  }
  ```
+ Scegliere **Execute query (Esegui query)** (il pulsante di riproduzione arancione).
+ Tutti i post di Nadia verranno visualizzati nel riquadro dei risultati a destra del riquadro della query. La schermata visualizzata dovrebbe risultare simile a quella nell'immagine seguente:

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

Se esegui la richiesta più volte, all'elenco verranno aggiunti più commenti.

## Conclusioni
<a name="conclusion"></a>

In questo tutorial, hai creato un'API che ci consente di manipolare gli oggetti Post in DynamoDB AWS AppSync utilizzando GraphQL. Per altre informazioni, vedere la [Informazioni di riferimento sui modelli di mappatura dei resolver](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference).

Per eseguire la pulizia, puoi eliminare l'API AppSync GraphQL dalla console.

Per eliminare la tabella DynamoDB e il ruolo IAM che hai creato per questo tutorial, puoi eseguire quanto segue per eliminare lo stack oppure visitare `AWSAppSyncTutorialForAmazonDynamoDB` la console ed eliminare CloudFormation lo stack:

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

# Utilizzo dei AWS Lambda resolver in AWS AppSync
<a name="tutorial-lambda-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Puoi usare AWS Lambda with AWS AppSync per risolvere qualsiasi campo GraphQL. Ad esempio, una query GraphQL potrebbe inviare una chiamata a un'istanza Amazon Relational Database Service (Amazon RDS) e una mutazione GraphQL potrebbe scrivere su un flusso Amazon Kinesis. In questa sezione, ti mostreremo come scrivere una funzione Lambda che esegue la logica aziendale basata sull'invocazione di un'operazione sul campo GraphQL.

## Creazione di una funzione Lambda
<a name="create-a-lam-function"></a>

L'esempio seguente mostra una funzione Lambda scritta in `Node.js` che esegue diverse operazioni sui post del blog come parte di un'applicazione per post di blog.

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

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

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

Questa funzione Lambda recupera un post per ID, aggiunge un post, recupera un elenco di post e recupera i post correlati per un determinato post.

 **Nota:** la funzione Lambda utilizza l'`switch`istruzione on `event.field` per determinare quale campo è attualmente in fase di risoluzione.

Crea questa funzione Lambda utilizzando la console di AWS gestione o uno AWS CloudFormation stack. Per creare la funzione da uno CloudFormation stack, puoi usare il seguente comando AWS Command Line Interface ()AWS CLI:

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

Puoi anche avviare lo CloudFormation stack nella AWS regione Stati Uniti occidentali (Oregon) dal tuo AWS account da qui:

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

## Configurare un'origine dati per Lambda
<a name="configure-data-source-for-lamlong"></a>

**Dopo aver creato la funzione Lambda, accedi all'API GraphQL nella AWS AppSync console, quindi scegli la scheda Data Sources.**

**Scegli **Crea origine dati**, inserisci un **nome di origine dati descrittivo** (ad esempio**Lambda**), quindi per **Tipo di origine dati**, scegli AWS Lambda funzione.** Per **Regione**, scegli la stessa regione della tua funzione. (Se hai creato la funzione dallo CloudFormation stack fornito, la funzione è probabilmente in **US-WEST-2**.) Per **Function ARN**, scegli l'Amazon Resource Name (ARN) della tua funzione Lambda.

Dopo aver scelto la funzione Lambda, puoi creare un nuovo ruolo AWS Identity and Access Management (IAM) (per il quale AWS AppSync assegna le autorizzazioni appropriate) o scegliere un ruolo esistente con la seguente politica in linea:

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

****  

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

------

È inoltre necessario impostare una relazione di fiducia con AWS AppSync per il ruolo IAM nel modo seguente:

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

****  

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

------

## Creare uno schema GraphQL
<a name="creating-a-graphql-schema"></a>

Ora che l'origine dati è connessa alla funzione Lambda, crea uno schema GraphQL.

Dall'editor di schemi nella AWS AppSync console, assicurati che lo schema corrisponda allo schema seguente:

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

## Configura i resolver
<a name="configuring-resolvers"></a>

Ora che hai registrato un'origine dati Lambda e uno schema GraphQL valido, puoi connettere i campi GraphQL all'origine dati Lambda utilizzando i resolver.

Per creare un resolver, avrai bisogno di modelli di mappatura. Per ulteriori informazioni sui modelli di mappatura, consulta. [Resolver Mapping Template Overview](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)

Per ulteriori informazioni sui modelli di mappatura Lambda, vedere. [Resolver mapping template reference for Lambda](resolver-mapping-template-reference-lambda.md#aws-appsync-resolver-mapping-template-reference-lambda)

In questo passaggio, si collega un resolver alla funzione Lambda per i seguenti campi:`getPost(id:ID!): Post`,, `allPosts: [Post]` e. `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!` `Post.relatedPosts: [Post]`

Dall'editor di schemi nella AWS AppSync console, sul lato destro, scegli **Attach** Resolver for. `getPost(id:ID!): Post`

Quindi, nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (**solo VTL).

Successivamente, scegli la tua fonte di dati Lambda. Nella sezione del **modello di mappatura della richiesta**, scegliere **Invoke And Forward Argomenti (Richiama e inoltra argomenti)**.

Modifica l'oggetto `payload` per aggiungere il nome del campo Il modello avrà un aspetto simile al seguente:

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

Nella sezione **modello di mappatura della risposta**, scegliere **Return Lambda Result (Restituisci risultato Lambda)**.

In questo caso, Useremo il modello di base senza apportare modifiche. Avrà un aspetto simile al seguente:

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

Scegli **Save** (Salva). Il primo resolver è stato ora collegato con successo. Ripeti questa operazione per i campi rimanenti nel modo seguente:

Per il modello di mappatura della richiesta `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!`:

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

Per il modello di mappatura della risposta `addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!`:

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

Per il modello di mappatura della richiesta `allPosts: [Post]`:

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

Per il modello di mappatura della risposta `allPosts: [Post]`:

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

Per il modello di mappatura della richiesta `Post.relatedPosts: [Post]`:

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

Per il modello di mappatura della risposta `Post.relatedPosts: [Post]`:

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

## Testa la tua API GraphQL
<a name="testing-your-graphql-api"></a>

Ora che la funzione Lambda è connessa ai resolver GraphQL, puoi eseguire alcune mutazioni e query usando la console o un'applicazione client.

Sul lato sinistro della AWS AppSync console, scegli **Queries**, quindi incolla il codice seguente:

### addPost Mutation
<a name="addpost-mutation"></a>

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

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

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

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

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

## Errori di restituzione
<a name="returning-errors"></a>

Qualsiasi risoluzione di campo può causare un errore. Con AWS AppSync, puoi generare errori dalle seguenti fonti:
+ Modello di mappatura della richiesta o della risposta
+ funzione Lambda

### Dal modello di mappatura
<a name="from-the-mapping-template"></a>

Per generare errori intenzionali, è possibile utilizzare il metodo `$utils.error` helper del modello Velocity Template Language (VTL). L'argomento può essere un valore `errorMessage`, un valore `errorType` e un valore facoltativo `data`. Il valore `data` è utile per restituire dati aggiuntivi al client quando è stato generato un errore. L'oggetto `data` verrà aggiunto a `errors` nella risposta finale di GraphQL.

L'esempio seguente mostra come usarlo nel modello di mappatura della risposta `Post.relatedPosts: [Post]`:

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

Ciò restituirà una risposta di GraphQL simile alla seguente:

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

Dove `allPosts[0].relatedPosts` è *null* a causa dell'errore e `errorMessage`, `errorType` e `data` sono presenti nell'oggetto `data.errors[0]`.

### Dalla funzione Lambda
<a name="from-the-lam-function"></a>

AWS AppSync comprende anche gli errori generati dalla funzione Lambda. Il modello di programmazione Lambda consente di generare errori *gestiti*. Se la funzione Lambda genera un errore, AWS AppSync non riesce a risolvere il campo corrente. Nella risposta viene impostato solo il messaggio di errore restituito da Lambda. Attualmente, non è possibile restituire dati estranei al client generando un errore dalla funzione Lambda.

 **Nota**: se la funzione Lambda genera un errore *non gestito*, AWS AppSync utilizza il messaggio di errore impostato da Lambda.

La funzione Lambda seguente genera un errore:

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

Ciò restituirà una risposta di GraphQL simile alla seguente:

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

## Caso d'uso avanzato: Batching
<a name="advanced-use-case-batching"></a>

La funzione Lambda in questo esempio ha un `relatedPosts` campo che restituisce un elenco di post correlati per un determinato post. Nelle query di esempio, l'invocazione del `allPosts` campo dalla funzione Lambda restituisce cinque post. Poiché abbiamo specificato che vogliamo risolvere anche `relatedPosts` per ogni post restituito, l'operazione `relatedPosts` sul campo viene richiamata cinque volte.

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

Anche se questo potrebbe non sembrare importante in questo esempio specifico, questo eccesso di recupero aggravato può compromettere rapidamente l'applicazione.

Se, ad esempio, dovessimo recuperare di nuovo `relatedPosts` sui `Posts` correlati restituiti nella stessa query, il numero di chiamate aumenterebbe notevolmente.

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

In questa query relativamente semplice, AWS AppSync richiamerebbe la funzione Lambda 1 \$1 5 \$1 25 = 31 volte.

Si tratta di una sfida piuttosto comune a cui si fa spesso riferimento come problema N\$11, (nel nostro caso, N = 5) che può determinare un aumento della latenza e dei costi dell'applicazione.

Un approccio alla soluzione di questo problema consiste nel raggruppare in batch richieste del resolver di campo simili. In questo esempio, invece di fare in modo che la funzione Lambda risolva un elenco di post correlati per un singolo post, potrebbe invece risolvere un elenco di post correlati per un determinato batch di post.

Per provare questa funzionalità, modifichiamo il resolver `Post.relatedPosts: [Post]` in un resolver abilitato per i batch.

Sul lato destro della AWS AppSync console, scegli il `Post.relatedPosts: [Post]` resolver esistente. Modifica il modello di mappatura della richiesta nel modo seguente:

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

Solo il campo `operation` è stato modificato da `Invoke` a `BatchInvoke`. Il campo payload ora diventa un array di tutto ciò che è specificato nel modello. In questo esempio, la funzione Lambda riceve quanto segue come input:

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

Quando `BatchInvoke` è specificata nel modello di mappatura delle richieste, la funzione Lambda riceve un elenco di richieste e restituisce un elenco di risultati.

In particolare, l'elenco dei risultati deve corrispondere alla dimensione e all'ordine delle voci del payload della richiesta in modo che AWS AppSync possa corrispondere ai risultati di conseguenza.

In questo esempio di batch, la funzione Lambda restituisce un batch di risultati come segue:

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

La seguente funzione Lambda in Node.js dimostra questa funzionalità di batch per il `Post.relatedPosts` campo come segue:

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

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

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

### Restituzione di errori individuali
<a name="returning-individual-errors"></a>

Gli esempi precedenti mostrano che è possibile restituire un singolo errore dalla funzione Lambda o generare un errore dai modelli di mappatura. Per le chiamate in batch, la generazione di un errore dalla funzione Lambda contrassegna un intero batch come fallito. Questo potrebbe essere accettabile per scenari specifici in cui si verifica un errore irreversibile, ad esempio una connessione non riuscita a un data store. Tuttavia, nei casi in cui alcuni elementi del batch abbiano esito positivo e altri falliscano, è possibile restituire sia errori che dati validi. Poiché AWS AppSync richiede la risposta in batch agli elementi dell'elenco che corrispondono alla dimensione originale del batch, è necessario definire una struttura di dati in grado di differenziare i dati validi da un errore.

*Ad esempio, se si prevede che la funzione Lambda restituisca un batch di post correlati, è possibile scegliere di restituire un elenco di `Response` oggetti in cui ogni oggetto contiene *dati* opzionali, campi ErrorMessage ed *ErrorType*.* La presenza del campo *errorMessage* indica un errore.

Il codice seguente mostra come aggiornare la funzione Lambda:

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

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

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

Per questo esempio, il seguente modello di mappatura delle risposte analizza ogni elemento della funzione Lambda e genera gli eventuali errori che si verificano:

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

Questo esempio restituirà una risposta di GraphQL simile alla seguente:

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

### Configurazione della dimensione massima di batch
<a name="configure-max-batch-size"></a>

Per impostazione predefinita, quando si utilizza`BatchInvoke`, AWS AppSync invia richieste alla funzione Lambda in batch composti da un massimo di cinque elementi. Puoi configurare la dimensione massima del batch dei tuoi resolver Lambda.

Per configurare la dimensione massima di batch su un resolver, usa il seguente comando in (): AWS Command Line Interface AWS CLI

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

**Nota**  
Quando si fornisce un modello di mappatura delle richieste, è necessario utilizzare l'`BatchInvoke`operazione per utilizzare il batch.

È inoltre possibile utilizzare il seguente comando per abilitare e configurare il batching su Direct Lambda Resolver:

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

### Configurazione della dimensione massima di batch con modelli VTL
<a name="configure-max-batch-size-vtl"></a>

Per i Lambda Resolver che dispongono di modelli VTL su richiesta, la dimensione massima del batch non avrà alcun effetto a meno che non l'abbiano specificata direttamente come operazione in VTL. `BatchInvoke` Allo stesso modo, se si esegue una mutazione di primo livello, il batching non viene eseguito per le mutazioni perché la specifica GraphQL richiede che le mutazioni parallele vengano eseguite in sequenza.

Ad esempio, prendiamo le seguenti mutazioni:

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

Usando la prima mutazione, possiamo crearne 10 `Items` come mostrato nel frammento seguente:

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

In questo esempio, non `Items` verranno raggruppati in un gruppo di 10 anche se la dimensione massima del batch è impostata su 10 nel Lambda Resolver. Verranno invece eseguiti in sequenza secondo le specifiche GraphQL.

Per eseguire una vera mutazione in batch, puoi seguire l'esempio seguente usando la seconda mutazione:

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

Per ulteriori informazioni sull'utilizzo del batching con Direct Lambda Resolver, vedere. [Resolver Lambda diretti](resolver-mapping-template-reference-lambda.md#direct-lambda-resolvers)

# Utilizzo dei OpenSearch resolver Amazon Service in AWS AppSync
<a name="tutorial-elasticsearch-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync supporta l'utilizzo di Amazon OpenSearch Service da domini che hai fornito nel tuo AWS account, a condizione che non esistano all'interno di un VPC. Dopo che sono stati assegnati i domini, è possibile connettersi a essi tramite un'origine dati, a quel punto è possibile configurare un resolver nello schema per eseguire operazioni di GraphQL, come ad esempio query, mutazioni e iscrizioni. Questo tutorial fornirà una descrizione di alcuni esempi comuni.

Per ulteriori informazioni, consulta il [Resolver Mapping Template Reference](resolver-mapping-template-reference-elasticsearch.md#aws-appsync-resolver-mapping-template-reference-elasticsearch) per. OpenSearch

## Impostazione One-Click
<a name="one-click-setup"></a>

Per configurare automaticamente un endpoint GraphQL con AWS AppSync Amazon OpenSearch Service configurato, puoi utilizzare questo modello: AWS CloudFormation 

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

Una volta completata la AWS CloudFormation distribuzione, puoi passare direttamente all'esecuzione di query e mutazioni [GraphQL](#tutorial-elasticsearch-resolvers-perform-queries-mutations).

## Crea un nuovo dominio di servizio OpenSearch
<a name="create-a-new-es-domain"></a>

Per iniziare con questo tutorial, è necessario un dominio di OpenSearch servizio esistente. Se non si dispone di un dominio, è possibile usare il campione seguente. Tieni presente che possono essere necessari fino a 15 minuti per la creazione di un dominio di OpenSearch servizio prima di poter passare all'integrazione con una fonte di AWS AppSync dati.

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

Puoi avviare il seguente AWS CloudFormation stack nella regione US West 2 (Oregon) nel tuo account: 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)

## Configura l'origine dei dati per il servizio OpenSearch
<a name="configure-data-source-for-es"></a>

Dopo aver creato il dominio OpenSearch Service, accedi all'API AWS AppSync GraphQL e scegli la scheda **Data Sources**. Scegli **Nuovo** e inserisci un nome descrittivo per l'origine dati, ad esempio «oss». Quindi scegli il ** OpenSearch dominio Amazon** per il **tipo di origine dati**, scegli la regione appropriata e dovresti vedere il tuo dominio OpenSearch di servizio elencato. Dopo averlo selezionato, puoi creare un nuovo ruolo e AWS AppSync assegnare le autorizzazioni appropriate al ruolo oppure puoi scegliere un ruolo esistente, che abbia la seguente politica in linea:

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

****  

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

------

Dovrai anche stabilire una relazione di fiducia con per quel ruolo: AWS AppSync 

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

****  

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

------

Inoltre, il dominio OpenSearch Service ha una propria **politica di accesso** che puoi modificare tramite la console di Amazon OpenSearch Service. Dovrai aggiungere una politica simile alla seguente, con le azioni e le risorse appropriate per il dominio del OpenSearch servizio. Tieni presente che il ruolo **Principal** sarà il ruolo dell'origine AppSync dati, che se lasci che sia la console a crearlo, può essere trovato nella console IAM.

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

****  

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

------

## Collegamento di un Resolver
<a name="connecting-a-resolver"></a>

Ora che l'origine dati è connessa al tuo dominio OpenSearch Service, puoi connetterla allo schema GraphQL con un resolver, come mostrato nell'esempio seguente:

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

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

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

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

Si noti che vi è un tipo `Post` definito dall'utente con un campo di `id`. Negli esempi seguenti, supponiamo che esista un processo (che può essere automatizzato) per inserire questo tipo nel dominio del OpenSearch servizio, che verrebbe mappato alla radice del percorso di`/post/_doc`, where `post` è l'indice. Da questo percorso principale, è possibile eseguire ricerche su singoli documenti, ricerche con caratteri jolly o ricerche su più documenti con `/id/post*` un percorso di. `/post/_search` **Ad esempio, se avete un altro tipo chiamato`User`, potete indicizzare i documenti in base a un nuovo indice chiamato`user`, quindi eseguire ricerche con un percorso di.** `/user/_search` 

Dall'editor dello schema nella AWS AppSync console, modifica lo `Posts` schema precedente per includere una `searchPosts` query:

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

Salvare lo schema. Sul lato destro, per `searchPosts`, scegliere **Allega resolver**. Nel **menu Azione**, scegli **Update runtime**, quindi scegli **Unit Resolver (solo VTL**). Quindi, scegli la fonte dei dati del OpenSearch servizio. Nella sezione **modello di mappatura della richiesta**, selezionare il menu a discesa per **Post della query** per ottenere un modello di base. Modificare il `path` in `/post/_search`. Avrà un aspetto simile al seguente:

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

Ciò presuppone che lo schema precedente contenga documenti che sono stati indicizzati in OpenSearch Service sotto il campo. `post` Se si strutturano i dati in modo diverso, è necessario aggiornarli di conseguenza.

Nella sezione del **modello di mappatura delle risposte**, è necessario specificare il `_source` filtro appropriato se si desidera recuperare i risultati dei dati da una query di OpenSearch servizio e tradurli in GraphQL. Usare il modello seguente:

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

## Modificare le ricerche
<a name="modifying-your-searches"></a>

Il modello di mappatura della richiesta precedente esegue una semplice query per tutti i record. Se si desidera eseguire la ricerca per un autore specifico e se si desidera, inoltre, che l'autore sia un argomento definito nella query di GraphQL, Nell'editor dello schema della console di AWS AppSync aggiungere una query `allPostsByAuthor`:

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

**Ora scegli **Attach resolver** e seleziona l'origine dati del OpenSearch servizio, ma usa il seguente esempio nel modello di mappatura delle risposte:**

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

Si noti che `body` viene popolato con una query del termine per il campo `author`, che è già passato attraverso il client come argomento. È possibile facoltativamente avere informazioni preinserite, come ad esempio il testo standard, o anche l'uso di altre [utilità](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference).

Se si usa questo resolver, compilare il **modello di mappatura della risposta** con le stesse informazioni dell'esempio precedente.

## Aggiungere dati al servizio OpenSearch
<a name="adding-data-to-es"></a>

Potresti voler aggiungere dati al tuo dominio di OpenSearch servizio come risultato di una mutazione GraphQL. Si tratta di un meccanismo potente per le ricerche e altri scopi. Poiché è possibile utilizzare gli abbonamenti GraphQL per [rendere i dati in tempo reale](aws-appsync-real-time-data.md), funge da meccanismo per notificare ai clienti gli aggiornamenti dei dati nel dominio di servizio. OpenSearch 

Torna alla pagina **Schema** nella AWS AppSync console e seleziona **Attach resolver** per la mutazione. `addPost()` Seleziona nuovamente l'origine dati del OpenSearch servizio e utilizza il seguente **modello di mappatura delle risposte** per lo schema: `Posts`

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

Così come illustrato in precedenza, questo è un esempio di come i dati potrebbero essere strutturati. Se si dispone di nomi di campi o di indici diversi, è necessario aggiornare il `path` e il `body` in base alle esigenze. In questo esempio viene inoltre illustrato come usare `$context.arguments` per popolare il modello dagli argomenti della mutazione di GraphQL.

Prima di procedere, utilizzate il seguente modello di mappatura delle risposte, che restituirà come output il risultato dell'operazione di mutazione o le informazioni sull'errore:

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

## Recupero di un documento individuale
<a name="retrieving-a-single-document"></a>

Infine, se si desidera usare la query `getPost(id:ID)` nello schema per restituire un documento singolo, trovare questa query nell'editor dello schema della console di AWS AppSync e selezionare **Allega resolver**. Seleziona nuovamente l'origine dati del OpenSearch servizio e utilizza il seguente modello di mappatura:

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

Poiché il `path` sopra indicato usa l'argomento `id` con un corpo vuoto, questo restituisce il singolo documento. Tuttavia, è necessario usare il seguente modello di mappatura della risposta, perché ora si restituisce un elemento singolo e non un elenco:

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

## Eseguire query e mutazioni
<a name="tutorial-elasticsearch-resolvers-perform-queries-mutations"></a>

Ora dovresti essere in grado di eseguire operazioni GraphQL sul tuo dominio di OpenSearch servizio. Vai alla scheda **Query** della AWS AppSync console e aggiungi un nuovo record:

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

Vedrai il risultato della mutazione sulla destra. Allo stesso modo, ora puoi eseguire una `searchPosts` query sul tuo dominio OpenSearch di servizio:

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

## Best practice
<a name="best-practices"></a>
+ OpenSearch Il servizio deve essere utilizzato per l'interrogazione dei dati, non come database principale. [Potresti voler utilizzare OpenSearch Service insieme ad Amazon DynamoDB, come indicato in Combined GraphQL Resolvers.](tutorial-combining-graphql-resolvers.md#aws-appsync-tutorial-combining-graphql-resolvers)
+ Concedi l'accesso al tuo dominio solo permettendo al ruolo di servizio di accedere al cluster. AWS AppSync 
+ È possibile iniziare con una soluzione di base in fase di sviluppo, con il cluster dal prezzo più basso, e quindi spostarsi in un cluster più grande con elevata disponibilità quando si passa alla produzione.

# Utilizzo di resolver locali in AWS AppSync
<a name="tutorial-local-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync consente di utilizzare fonti di dati supportate (AWS Lambda Amazon DynamoDB o OpenSearch Amazon Service) per eseguire varie operazioni. In alcuni scenari, tuttavia, una chiamata a un'origine dati supportata può non essere necessaria.

È in queste situazioni che il resolver locale risulta utile. Invece di chiamare un'origine dati remota, il resolver locale **inoltra** semplicemente il risultato del modello di mappatura della richiesta al modello di mappatura della risposta. La risoluzione del campo rimarrà in AWS AppSync.

I resolver locali sono utili per diversi casi d'uso. Quello più comune è la pubblicazione di notifiche senza attivare una chiamata a un'origine dati. Per dimostrare questo caso d'uso creeremo un'applicazione di paging in cui gli utenti possono inviarsi messaggi. In questo esempio vengono usate le *sottoscrizioni*. Quindi, se non hai familiarità con le *sottoscrizioni*, puoi seguire il tutorial [Dati in tempo reale](aws-appsync-real-time-data.md).

## Creare l'applicazione di paging
<a name="create-the-paging-application"></a>

In questa applicazione di paging, i client possono iscriversi a una casella di posta e inviare pagine ad altri client. Ogni pagina include un messaggio. Lo schema è il seguente:

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

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

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

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

type Query {
    me: String
}
```

Colleghiamo ora un resolver al campo `Mutation.page`. Nel riquadro **Schema**, fai clic su *Attach Resolver (Collega resolver)* accanto alla definizione del campo nel riquadro destro. Crea una nuova fonte di dati di tipo *Nessuno* e assegnale un nome. *PageDataSource*

Per il modello di mappatura della richiesta immetti:

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

E per il modello di mappatura della risposta, seleziona l'impostazione predefinita *Forward the result (Inoltra il risultato)*. Salva il resolver. L'applicazione è pronta per iniziare a inviare messaggi.

## Inviare e iscriversi alle pagine
<a name="send-and-subscribe-to-pages"></a>

Perché i client possano ricevere le pagine, devono prima iscriversi a una casella di posta.

Nel riquadro **Queries (Query)** eseguiamo la sottoscrizione `inbox`:

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

 *Nadia* riceverà le pagine ogni volta che la mutazione `Mutation.page` viene richiamata. La mutazione viene richiamata tramite la relativa esecuzione:

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

Abbiamo appena dimostrato l'uso dei resolver locali, inviando una pagina e ricevendola senza uscire da AWS AppSync.

# Combinazione di resolver GraphQL in AWS AppSync
<a name="tutorial-combining-graphql-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

I resolver e i campi in uno schema di GraphQL hanno un rapporto 1:1 con un elevato grado di flessibilità. Poiché un'origine dati è configurata su un resolver indipendentemente dallo schema, c'è la possibilità di risolvere o manipolare i tipi di GraphQL tramite diverse origini dati, mescolandole o combinandole in uno schema per soddisfare al meglio le esigenze.

Gli scenari di esempio seguenti mostrano come combinare e abbinare le fonti di dati nello schema. Prima di iniziare, ti consigliamo di acquisire dimestichezza con la configurazione di sorgenti di dati e resolver per Amazon AWS Lambda DynamoDB e Amazon OpenSearch Service, come descritto nei tutorial precedenti.

## Schema di esempio
<a name="example-schema"></a>

Lo schema seguente ha un tipo `Post` con 3 `Query` operazioni e 3 `Mutation` operazioni definite:

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

In questo esempio si dispone di un totale di 6 resolver da allegare. Un modo possibile sarebbe quello di far sì che tutti questi dati provengano da una tabella Amazon DynamoDB, `Posts` chiamata, `AllPosts` dove esegue una scansione `searchPosts` ed esegue una query, come indicato nel [DynamoDB Resolver Mapping Template Reference](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb). Tuttavia, esistono alternative per soddisfare le esigenze aziendali, come la risoluzione di queste query GraphQL da Lambda o Service. OpenSearch 

## Modificare i dati tramite resolver
<a name="alter-data-through-resolvers"></a>

Potrebbe essere necessario restituire i risultati di un database come DynamoDB (o Amazon Aurora) ai client con alcuni attributi modificati. Questo potrebbe essere dovuto alla formattazione dei tipi di dati, ad esempio differenze di timestamp sui client, oppure gestire problemi di retrocompatibilità. A scopo illustrativo, nell'esempio seguente, una AWS Lambda funzione manipola i voti positivi e negativi per i post del blog assegnando loro numeri casuali ogni volta che viene richiamato il resolver GraphQL:

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

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

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

Questa è una funzione di Lambda perfettamente valida e potrebbe essere associata al campo `AllPosts` nello schema di GraphQL in modo che qualsiasi query che restituisce tutti i risultati ottenga numeri casuali per i voti in alto/in basso.

## DynamoDB e Service OpenSearch
<a name="ddb-and-es"></a>

Per alcune applicazioni, è possibile eseguire mutazioni o semplici query di ricerca su DynamoDB e disporre di un processo in background per trasferire i documenti al Service. OpenSearch È quindi possibile collegare semplicemente il `searchPosts` Resolver all'origine dati del OpenSearch servizio e restituire i risultati della ricerca (dai dati originati in DynamoDB) utilizzando una query GraphQL. Questo può essere estremamente potente quando si aggiungono operazioni di ricerca avanzate per applicazioni come parole chiave, combinazioni ambigue di parole o anche ricerche geospaziali. Il trasferimento di dati da DynamoDB può essere effettuato tramite un processo ETL o, in alternativa, è possibile eseguire lo streaming da DynamoDB utilizzando Lambda. Puoi lanciare un esempio completo di ciò utilizzando il seguente AWS CloudFormation stack nella regione US West 2 (Oregon) nel tuo account: AWS 

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

Lo schema in questo esempio consente di aggiungere post utilizzando un resolver DynamoDB come segue:

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

Questo scrive i dati su DynamoDB, che poi trasmette i dati tramite Lambda ad OpenSearch Amazon Service, dove puoi cercare tutti i post in base a campi diversi. Ad esempio, poiché i dati si trovano in Amazon OpenSearch Service, puoi cercare nei campi dell'autore o del contenuto con testo in formato libero, anche con spazi, come segue:

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

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

Poiché i dati vengono scritti direttamente su DynamoDB, è comunque possibile eseguire operazioni efficienti di ricerca di elenchi o elementi sulla tabella con le query and. `allPosts{...}` `singlePost{...}` Questo stack utilizza il seguente codice di esempio per i flussi DynamoDB:

 **Nota**: questo codice è solo a scopo esemplificativo.

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

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

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

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

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

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

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

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

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

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

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

Puoi quindi utilizzare i flussi DynamoDB per collegarla a una tabella DynamoDB con una chiave primaria di e qualsiasi modifica all'origine `id` di DynamoDB verrà trasferita nel tuo dominio di servizio. OpenSearch Per ulteriori informazioni sulla configurazione di questa funzionalità, consulta [Documentazione dei flussi di DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html).

# Utilizzo delle operazioni batch di DynamoDB in AWS AppSync
<a name="tutorial-dynamodb-batch"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync supporta l'utilizzo di operazioni batch di Amazon DynamoDB su una o più tabelle in una singola regione. Le operazioni supportate sono `BatchGetItem`, `BatchPutItem` e `BatchDeleteItem`. Usando queste caratteristiche in AWS AppSync, è possibile eseguire attività quali ad esempio:
+ Passare un elenco di chiavi in una singola query e restituire i risultati da una tabella
+ Leggere i record da una o più tabelle in un'unica query
+ Scrivere record in blocco in una o più tabelle
+ Scrivere o eliminare in forma condizionata record in più tabelle che possono essere relazionate

L'utilizzo delle operazioni batch con DynamoDB AWS AppSync in è una tecnica avanzata che richiede un po' di riflessione e conoscenza in più delle operazioni di backend e delle strutture delle tabelle. Inoltre, le operazioni in batch AWS AppSync presentano due differenze fondamentali rispetto alle operazioni non in batch:
+ Il ruolo dell'origine dati deve disporre delle autorizzazioni per tutte le tabelle a cui il resolver effettua l'accesso.
+ La specifica della tabella per un resolver fa parte del modello di mappatura.

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

Come altri resolver, è necessario creare un'origine dati in AWS AppSync e creare un ruolo o utilizzarne uno esistente. Poiché le operazioni batch richiedono autorizzazioni diverse sulle tabelle DynamoDB, è necessario concedere ai ruoli configurati le autorizzazioni per le azioni di lettura o scrittura:

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

****  

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

------

 **Nota**: i ruoli sono legati alle fonti di dati in AWS AppSync e i resolver sui campi vengono richiamati su un'origine dati. Le fonti di dati configurate per il recupero con DynamoDB hanno solo una tabella specificata, per semplificare la configurazione. Pertanto, quando si esegue un'operazione di batch su più tabelle in un singolo resolver, che è una delle attività più avanzate, è necessario concedere il ruolo sull'accesso all'origine dati a tutte le tabelle con cui interagirà il resolver. Questo potrebbe essere fatto nel campo **Risorsa** nella policy di IAM indicata in precedenza. Viene effettuata la configurazione delle tabelle per effettuare chiamate in batch nel modello di resolver, che verrà descritto di seguito.

## Origine dati
<a name="data-source"></a>

Per semplicità, useremo la stessa origine dati per tutti i resolver usati in questo tutorial. Nella scheda **Sorgenti dati**, crea una nuova origine dati DynamoDB e assegnale un nome. **BatchTutorial** Per la tabella è possibile specificare un qualsiasi nome, perché i nomi delle tabelle sono specificati come parte del modello di mappatura delle richieste per le operazioni in batch. La tabella si chiamerà `empty`.

Per questo tutorial, funzionerà qualsiasi ruolo con le seguenti policy inline:

## Batch a tabella singola
<a name="single-table-batch"></a>

**avvertimento**  
`BatchPutItem`e non `BatchDeleteItem` sono supportati se utilizzati per il rilevamento e la risoluzione dei conflitti. Queste impostazioni devono essere disabilitate per evitare possibili errori.

Per questo esempio, si supponga di disporre di una tabella singola denominata **Post** a cui si desidera aggiungere o rimuovere voci con operazioni in batch. Usa lo schema seguente, notando che per la query passeremo un elenco di IDs:

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

input PostInput {
    id: ID!
    title: String
}

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

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

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

Allegare un resolver al campo `batchAdd()` con il seguente **Modello di mappatura della richiesta**. Questo prende automaticamente ogni voce nel tipo `input PostInput`di GraphQL e crea una mappa, che è necessaria per l'operazione `BatchPutItem`:

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

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

In questo caso, il **Modello di mappatura della risposta** è un semplice passaggio, ma notare che il nome della tabella viene aggiunto come `..data.Posts` all'oggetto del contesto come segue:

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

Ora accedere alla pagina **Query** della console di AWS AppSync ed eseguire la seguente mutazione **batchAdd**:

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

**Dovresti vedere i risultati stampati sullo schermo e puoi convalidare in modo indipendente tramite la console DynamoDB che entrambi i valori sono stati scritti nella tabella Posts.**

Successivamente, allegare un resolver al campo `batchGet()` con il seguente **Modello di mappatura della richiesta**. Questo prende automaticamente ogni voce nel tipo `ids:[]` di GraphQL e crea una mappa, che risulta necessaria per l'operazione `BatchGetItem`:

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

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

Il **Modello di mappatura della risposta** è ancora un semplice passaggio, con di nuovo il nome della tabella che viene aggiunto come `..data.Posts` all'oggetto del contesto:

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

Ora tornare alla pagina **Query** della console di AWS AppSync ed eseguire la seguente **Query di batchAdd**:

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

Questo dovrebbe restituire i risultati per i due valori `id` aggiunti in precedenza. Si noti che un valore `null` è stato restituito per `id` con un valore di `3`. Questo perché non era presente nessun record nella tabella **Post** che avesse ancora tale valore. Tieni inoltre presente che AWS AppSync restituisce i risultati nello stesso ordine delle chiavi passate alla query, una funzionalità aggiuntiva che viene eseguita per conto AWS AppSync tuo. Pertanto, se si passa a `batchGet(ids:[1,3,2)`, si visualizzerà l'ordine modificato. È inoltre possibile sapere quale `id` ha restituito un valore `null`.

Infine, allegare un resolver al campo `batchDelete()` con il seguente **Modello di mappatura della richiesta**. Questo prende automaticamente ogni voce nel tipo `ids:[]` di GraphQL e crea una mappa, che risulta necessaria per l'operazione `BatchGetItem`:

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

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

Il **Modello di mappatura della risposta** è ancora un semplice passaggio, con di nuovo il nome della tabella che viene aggiunto come `..data.Posts` all'oggetto del contesto:

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

Ora tornare alla pagina **Query** della console di AWS AppSync ed eseguire la seguente mutazione **batchDelete**:

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

I record con `id` `1` e `2` dovrebbero essere eliminati. Se si esegue nuovamente la query `batchGet()` da una versione precedente, questi devono restituire `null`.

## Batch a tabella multipla
<a name="multi-table-batch"></a>

**avvertimento**  
`BatchPutItem`e non `BatchDeleteItem` sono supportati se utilizzati per il rilevamento e la risoluzione dei conflitti. Queste impostazioni devono essere disabilitate per evitare possibili errori.

AWS AppSync consente inoltre di eseguire operazioni in batch su più tabelle. L'applicazione seguente è più complessa da costruire. Si immagini di creare un'app per la salute degli animali, in cui i sensori segnalano la posizione dell'animale domestico e la temperatura corporea. I sensori sono dotati di batteria e tentano di connettersi alla rete a distanza di pochi minuti. Quando un sensore stabilisce una connessione, invia le sue letture alla nostra AWS AppSync API. I trigger quindi analizzano i dati in modo da presentare un pannello di controllo al proprietario dell'animale domestico, focalizzando l'attenzione sulla rappresentazione delle interazioni tra il sensore e l'archivio di dati di back-end.

**Come prerequisito, creiamo prima due tabelle DynamoDB**;** LocationReadings memorizzerà le letture della posizione del sensore e TemperatureReadings memorizzerà le letture della temperatura del sensore.** Entrambe le tabelle condividono la stessa struttura della chiave primaria: la chiave di partizione `sensorId (String)` e la chiave di ordinamento `timestamp (String)`.

Utilizzare il seguente schema di GraphQL:

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

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

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

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

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

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

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

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

### BatchPutItem - Registrazione delle letture del sensore
<a name="batchputitem-recording-sensor-readings"></a>

I sensori devono essere in grado di inviare le loro letture una volta stabilita la connessione a Internet. Il campo `Mutation.recordReadings` di GraphQL è l'API che si userà per farlo. Allegare un resolver per portare alla vita l'API.

Selezionare **Attach (Allega)** vicino al campo `Mutation.recordReadings`. Sulla schermata successiva, scegliere la stessa origine dati `BatchTutorial` creata all'inizio di questo tutorial.

Aggiungere il seguente modello di mappatura della richiesta.

 **Modello di mappatura della richiesta** 

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

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

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

Come si può vedere, l'operazione `BatchPutItem` consente di specificare più tabelle.

Usare il seguente modello di mappatura della risposta.

 **Modello di mappatura della risposta** 

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

Con le operazioni in batch, possono essere presenti entrambi gli errori e i risultati restituiti dalla chiamata. In questo caso, è possibile eseguire liberamente ulteriori operazioni di gestione degli errori.

 **Nota**: l'uso di `$utils.appendError()` è analogo a quello di `$util.error()`, con la differenza principale che non interrompe la valutazione del modello di mappatura. Al contrario, segnala che si è verificato un errore con il campo, ma consente al modello di essere valutato e, di conseguenza, di restituire i dati al chiamante. Si consiglia di usare `$utils.appendError()` quando un'applicazione deve restituire risultati parziali.

Salva il resolver e vai alla pagina **Query della console**. AWS AppSync Inviare alcune letture del sensore.

Eseguire la mutazione seguente:

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

Sono state inviate 10 letture del sensore in una sola mutazione, con letture suddivise in due tabelle. ****Utilizza la console DynamoDB per verificare che i dati vengano visualizzati nelle tabelle LocationReadings e Temperaturereadings.****

### BatchDeleteItem - Eliminazione delle letture del sensore
<a name="batchdeleteitem-deleting-sensor-readings"></a>

Analogamente, sarebbe necessario anche eliminare i batch delle letture del sensore. Utilizzare il campo `Mutation.deleteReadings` di GraphQL per questo scopo. Selezionare **Attach (Allega)** vicino al campo `Mutation.recordReadings`. Sulla schermata successiva, scegliere la stessa origine dati `BatchTutorial` creata all'inizio di questo tutorial.

Utilizzare il seguente modello di mappatura della richiesta.

 **Modello di mappatura della richiesta** 

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

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

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

Il modello di mappatura della risposta è lo stesso utilizzato per `Mutation.recordReadings`.

 **Modello di mappatura della risposta** 

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

Salva il resolver e vai alla pagina **Query della console**. AWS AppSync Ora, eliminare un paio di letture del sensore.

Eseguire la mutazione seguente:

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

****Verifica tramite la console DynamoDB che queste due letture siano state eliminate dalle tabelle LocationReadings e TemperatureReadings.****

### BatchGetItem - Recupera le letture
<a name="batchgetitem-retrieve-readings"></a>

Un'altra operazione comune per l'app Pet Health sarebbe quella di recuperare le letture per un sensore in un determinato momento. Allegare un resolver al campo `Query.getReadings` di GraphQL sullo schema. Selezionare **Attach (Allega)** e, sulla schermata successiva, scegliere la stessa origine dati `BatchTutorial` creata all'inizio del tutorial.

Aggiungere il seguente modello di mappatura della richiesta.

 **Modello di mappatura della richiesta** 

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

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

Nota che ora stiamo usando l'**BatchGetItem**operazione.

Il modello di mappatura della risposta sarà leggermente diverso perché è stato scelto di restituire un elenco `SensorReading`. Mappare il risultato della chiamata nella forma desiderata.

 **Modello di mappatura della risposta** 

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

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

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

$util.toJson($sensorReadings)
```

Salva il resolver e vai alla pagina **Query** della console. AWS AppSync Ora, recuperare le letture del sensore.

Eseguire la query seguente:

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

Abbiamo dimostrato con successo l'uso delle operazioni batch di DynamoDB utilizzando. AWS AppSync

## Gestione errori
<a name="error-handling"></a>

Nel AWS AppSync, le operazioni sulle sorgenti dati a volte possono restituire risultati parziali. Risultati parziali è il termine che si userà per indicare quando l'output di un'operazione comprende alcuni dati e un errore. Poiché la gestione degli errori è intrinsecamente specifica dell'applicazione, AWS AppSync offre la possibilità di gestire gli errori nel modello di mappatura della risposta. L'errore di chiamata del resolver, se presente, è disponibile nel contesto come `$ctx.error`. Gli errori di chiamata comprendono sempre un messaggio e un tipo, accessibili come proprietà `$ctx.error.message` e `$ctx.error.type`. Durante la chiamata del modello di mappatura della risposta, è possibile gestire i risultati parziali in tre modi:

1. assumere l'errore di chiamata semplicemente restituendo i dati

1. generare un errore (usando `$util.error(...)`) arrestando la valutazione del modello di mappatura della risposta, che non restituisce nessun dato.

1. accodare un errore (usando `$util.appendError(...)`) e restituire anche i dati

Ora è opportuno dimostrare ciascuno dei tre punti sopra indicati con le operazioni in batch di DynamoDB.

### Operazioni di batch di DynamoDB
<a name="dynamodb-batch-operations"></a>

Con le operazioni di batch di DynamoDB, è possibile che un batch sia completato parzialmente. Ovvero, è possibile che alcune delle chiavi o voci richieste non vengano elaborate. Se non AWS AppSync è in grado di completare un batch, nel contesto verranno impostati elementi non elaborati e un errore di invocazione.

La gestione degli errori verrà realizzata tramite la configurazione del campo `Query.getReadings` dall'operazione `BatchGetItem` dalla sezione precedente di questo tutorial. In questo momento, è opportuno supporre che durante l'esecuzione del campo `Query.getReadings`, la tabella `temperatureReadings` DynamoDB esegua un throughput assegnato. DynamoDB ha generato **ProvisionedThroughputExceededException**un al secondo tentativo AWS AppSync per elaborare gli elementi rimanenti del batch.

Il seguente JSON rappresenta il contesto serializzato dopo la chiamata del batch di DynamoDB prima che il modello di mappatura della risposta venisse valutato.

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

Alcune cose da tenere presente in questo contesto:
+ **l'errore di invocazione è stato impostato nel contesto in `$ctx.error` by AWS AppSync e il tipo di errore è stato impostato su DynamoDB:. ProvisionedThroughputExceededException**
+ i risultati sono mappati per tabella in `$ctx.result.data`, anche se è presente un errore
+ le chiavi che non sono state elaborate sono disponibili in `$ctx.result.data.unprocessedKeys`. Qui, non AWS AppSync è stato possibile recuperare l'elemento con la chiave (SensorID:1, timestamp:2018-02-01T 17:21:05.000 \$1 08:00) a causa dell'insufficiente velocità di trasmissione della tabella.

 **Nota**: per `BatchPutItem`, è `$ctx.result.data.unprocessedItems`. Per `BatchDeleteItem`, è `$ctx.result.data.unprocessedKeys`.

È possibile gestire l'errore in tre modi diversi.

#### 1. Assumere l'errore di chiamata
<a name="swallowing-the-invocation-error"></a>

Restituire i dati senza gestire l'errore di chiamata assume efficacemente l'errore. In questo modo, il risultato per il campo di GraphQL è sempre positivo.

Il modello di mappatura della risposta che viene scritto è familiare e si focalizza solo sui dati del risultato.

Modello di mappatura della risposta:

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

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

Non verrà aggiunto nessun errore alla risposta di errore poiché sono stati eseguiti solo i dati.

#### 2. Generazione di un errore per interrompere l'esecuzione del modello
<a name="raising-an-error-to-abort-the-template-execution"></a>

Quando i guasti parziali devono essere trattati come guasti totali dalla prospettiva del client, è possibile interrompere l'esecuzione del modello per evitare di restituire i dati. Il metodo di utilità `$util.error(...)` raggiunge esattamente questo comportamento.

Modello di mappatura della risposta:

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

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

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

Sebbene alcuni risultati possano essere stati restituiti dall'operazione in batch di DynamoDB, si è scelto di generare un errore in modo tale che il campo `getReadings` di GraphQL sia nullo e l'errore venga aggiunto al blocco di *errori* della risposta GraphQL.

#### 3. Aggiunta di un errore per restituire sia i dati sia gli errori
<a name="appending-an-error-to-return-both-data-and-errors"></a>

In alcuni casi, per fornire una migliore esperienza utente, le applicazioni possono restituire risultati parziali e notificare ai client le voci non elaborate. I client possono decidere di implementare un nuovo tentativo oppure di rimandare l'errore all'utente finale. Il `$util.appendError(...)` è il metodo di utilità che consente questo comportamento, permettendo al designer dell'applicazione di accodare gli errori nel contesto senza interferire con la valutazione del modello. Dopo aver valutato il modello, AWS AppSync elaborerà eventuali errori di contesto aggiungendoli al blocco errors della risposta GraphQL.

Modello di mappatura della risposta:

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

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

Sono stati inoltrati sia l'errore di chiamata sia l'elemento unprocessedKeys all'interno del blocco di errori della risposta di GraphQL. Anche il campo `getReadings` restituisce dati parziali dalla tabella **locationReadings** come è possibile vedere nella risposta di seguito.

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

# Esecuzione di transazioni DynamoDB in AWS AppSync
<a name="tutorial-dynamodb-transact"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync supporta l'utilizzo di operazioni di transazione Amazon DynamoDB su una o più tabelle in una singola regione. Le operazioni supportate sono `TransactGetItems` e `TransactWriteItems`. Utilizzando queste funzionalità in AWS AppSync, puoi eseguire attività come:
+ Passare un elenco di chiavi in una singola query e restituire i risultati da una tabella
+ Leggere i record da una o più tabelle in un'unica query
+ Scrivi i record della transazione su una o più tabelle in qualsiasi all-or-nothing modo
+ Eseguire transazioni quando alcune condizioni sono soddisfatte

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

Come altri resolver, è necessario creare un'origine dati in AWS AppSync e creare un ruolo o utilizzarne uno esistente. Poiché le operazioni di transazione richiedono autorizzazioni diverse sulle tabelle DynamoDB, è necessario concedere ai ruoli configurati le autorizzazioni per le azioni di lettura o scrittura:

------
#### [ 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**: i ruoli sono legati alle fonti di dati in AWS AppSync e i resolver sui campi vengono richiamati su un'origine dati. Le fonti di dati configurate per il recupero con DynamoDB hanno solo una tabella specificata, per semplificare la configurazione. Pertanto, quando si esegue un'operazione di transazione su più tabelle in un singolo resolver, che è una delle attività più avanzate, è necessario concedere il ruolo sull'accesso all'origine dati a tutte le tabelle con cui interagirà il resolver. Questo potrebbe essere fatto nel campo **Risorsa** nella policy di IAM indicata in precedenza. Viene effettuata la configurazione delle chiamate di transazione nelle tabelle nel modello di resolver, che verrà descritto di seguito.

## Origine dati
<a name="data-source"></a>

Per semplicità, useremo la stessa origine dati per tutti i resolver usati in questo tutorial. Nella scheda **Sorgenti dati**, crea una nuova origine dati DynamoDB e assegnale un nome. **TransactTutorial** Per la tabella è possibile specificare un qualsiasi nome, perché i nomi delle tabelle sono specificati come parte del modello di mappatura delle richieste per le operazioni di transazione. La tabella si chiamerà `empty`.

Avremo due tabelle denominate **SavingAccounts** e **CheckingAccounts**, entrambe con `accountNumber` come chiave di partizione e una tabella **transactionHistory** con `transactionId` come chiave di partizione.

Per questo tutorial, funzionerà qualsiasi ruolo con le seguenti policy inline: Sostituisci `region` e `accountId` con la tua regione e l'ID dell'account:

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

------

## Transazioni
<a name="transactions"></a>

Per questo esempio, il contesto è una classica transazione bancaria, in cui useremo `TransactWriteItems` per:
+ Trasferire denaro dai conti di deposito ai conti correnti
+ Generare nuovi record di transazione per ogni transazione

Quindi, useremo `TransactGetItems` per recuperare i dettagli dai conti di deposito ai conti correnti.

**avvertimento**  
`TransactWriteItems`non è supportato se utilizzato per il rilevamento e la risoluzione dei conflitti. Queste impostazioni devono essere disabilitate per evitare possibili errori.

Definiamo il nostro schema GraphQL come segue:

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

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

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

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

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

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

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

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

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

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

### TransactWriteItems - Compila gli account
<a name="transactwriteitems-populate-accounts"></a>

Al fine di trasferire denaro tra gli account, abbiamo bisogno di popolare la tabella con i dettagli. Per farlo, useremo l'operazione GraphQL `Mutation.populateAccounts`.

Nella sezione Schema, fai clic su **Allega** accanto all'`Mutation.populateAccounts`operazione. Vai a VTL Unit Resolvers, quindi scegli la stessa fonte di dati. `TransactTutorial`

Usa modello di mappatura della richiesta seguente:

 **Modello di mappatura della richiesta** 

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

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

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

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

E il seguente modello di mappatura della risposta:

 **Modello di mappatura della risposta** 

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

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

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

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

$util.toJson($transactionResult)
```

Salva il resolver e vai alla sezione **Queries** della console per popolare gli account. AWS AppSync 

Eseguire la mutazione seguente:

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

Abbiamo popolato 3 conti di deposito e 3 conti correnti in una mutazione.

****Utilizza la console DynamoDB per verificare che i dati vengano visualizzati nelle tabelle SavingAccounts e CheckingAccounts.****

### TransactWriteItems - Trasferimento di denaro
<a name="transactwriteitems-transfer-money"></a>

Allegare un resolver alla mutazione `transferMoney` con il seguente **Modello di mappatura della richiesta**. Tenere presente che i valori di `amounts`, `savingAccountNumbers` e `checkingAccountNumbers` sono gli stessi.

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

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

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

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

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

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

Avremo 3 transazioni bancarie in una singola operazione `TransactWriteItems`. Utilizzare il seguente **modello di mappatura della risposta**:

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

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

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

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

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

$util.toJson($transactionResult)
```

Passare ora alla sezione **Query** della console AWS AppSync ed eseguire la mutazione **transferMoney** come segue:

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

Abbiamo inviato 2 transazioni bancarie in una mutazione. ****Utilizza la console DynamoDB per verificare che i dati vengano visualizzati nelle tabelle **SavingAccounts**, CheckingAccounts e TransactionHistory.****

### TransactGetItems - Recupera account
<a name="transactgetitems-retrieve-accounts"></a>

Al fine di recuperare i dettagli dai conti di deposito ai conti correnti in una singola richiesta transazionale, allegheremo un resolver all'operazione GraphQL `Query.getAccounts` sul nostro schema. Seleziona **Allega**, vai a VTL Unit Resolvers, quindi nella schermata successiva, scegli la stessa fonte di `TransactTutorial` dati creata all'inizio del tutorial. Configurare i modelli come segue:

 **Modello di mappatura della richiesta** 

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

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

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

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

 **Modello di mappatura della risposta** 

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

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

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

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

$util.toJson($transactionResult)
```

**Salva il resolver e vai alle sezioni Queries della console.** AWS AppSync Per recuperare i conti di deposito ai conti correnti, eseguire la seguente query:

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

Abbiamo dimostrato con successo l'uso delle transazioni DynamoDB utilizzando. AWS AppSync

# Utilizzo di resolver HTTP in AWS AppSync
<a name="tutorial-http-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync consente di utilizzare fonti di dati supportate (ad esempio Amazon DynamoDB AWS Lambda, Amazon Service o OpenSearch Amazon Aurora) per eseguire varie operazioni, oltre a qualsiasi endpoint HTTP arbitrario per risolvere i campi GraphQL. Quando gli endpoint HTTP sono disponibili, è possibile connettersi a questi utilizzando un'origine dati. Quindi, è possibile configurare un resolver nello schema per eseguire operazioni GraphQL, ad esempio query, mutazioni e sottoscrizioni. Questo tutorial fornirà una guida di alcuni esempi comuni.

In questo tutorial utilizzi un'API REST (creata utilizzando Amazon API Gateway e Lambda) con un endpoint GraphQL AWS AppSync .

## Impostazione One-Click
<a name="one-click-setup"></a>

Se desideri configurare automaticamente un endpoint GraphQL AWS AppSync con un endpoint HTTP configurato (utilizzando Amazon API Gateway e Lambda), puoi utilizzare il seguente modello: AWS CloudFormation 

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

## Creazione di un'API REST
<a name="creating-a-rest-api"></a>

Puoi utilizzare il seguente AWS CloudFormation modello per configurare un endpoint REST adatto a questo 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)

Lo AWS CloudFormation stack esegue i seguenti passaggi:

1. Imposta una funzione Lambda che contiene la logica di business per il tuo microservizio.

1. Imposta un'API REST API Gateway con la seguente combinazione endpoint/method/content di tipi:


****  

| Percorso risorsa API | Metodo HTTP | Tipo di contenuto supportato | 
| --- | --- | --- | 
|  /v1/utenti  |  POST  |  application/json  | 
|  /v1/utenti  |  GET  |  application/json  | 
|  /v1/utenti/1  |  GET  |  application/json  | 
|  /v1/utenti/1  |  PUT  |  application/json  | 
|  /v1/utenti/1  |  DELETE  |  application/json  | 

## Creazione dell'API GraphQL
<a name="creating-your-graphql-api"></a>

Per creare l'API GraphQL in: AWS AppSync
+ Apri la AWS AppSync console e scegli **Crea API**.
+ Per il nome API, digita `UserData`.
+ Scegli **Custom schema (Schema personalizzato)**.
+ Scegli **Create** (Crea).

La AWS AppSync console crea una nuova API GraphQL per te utilizzando la modalità di autenticazione della chiave API. Puoi usare la console per configurare ulteriormente l'API GraphQL ed eseguire query sull'API per il resto di questo tutorial.

## Creazione di uno schema GraphQL
<a name="creating-a-graphql-schema"></a>

Ora che hai un'API GraphQL, è possibile creare uno schema GraphQL. Dall'editor dello schema nella AWS AppSync console, assicurati che lo schema corrisponda al seguente schema:

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

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

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

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

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

## Configurazione dell'origine dati HTTP
<a name="configure-your-http-data-source"></a>

Per configurare l'origine dati HTTP, effettua le seguenti operazioni:
+ Nella **DataSources**scheda, scegli **Nuovo**, quindi digita un nome descrittivo per l'origine dati (ad esempio,`HTTP`).
+ In **Data source type (Tipo di origine dati)**, scegliere **HTTP**.
+ Imposta l'endpoint sull'endpoint API Gateway che viene creato. Assicurarsi di non includere il nome della fase come parte dell'endpoint.

 **Nota:** al momento solo gli endpoint pubblici sono supportati da AWS AppSync.

 **Nota:** per ulteriori informazioni sulle autorità di certificazione riconosciute dal AWS AppSync servizio, consulta [Autorità di certificazione (CA) riconosciute da AWS AppSync per](http-cert-authorities.md#aws-appsync-http-certificate-authorities) gli endpoint HTTPS.

## Configurazione dei resolver
<a name="configuring-resolvers"></a>

In questa fase, è possibile connettere l'origine dati http alla query **getUser**.

Per configurare il resolver:
+ Scegli la scheda **Schema**.
+ Nel riquadro **Data types (Tipi di dati)** a destra sotto il tipo **Query**, individuare il campo **getUser** e scegliere **Attach (Collega)**.
+ In **Data source name (Nome origine dati)**, scegliere **HTTP**.
+ In **Configure the request mapping template (Configura il modello di mappatura della richiesta)**, incollare il codice seguente:

```
{
    "version": "2018-05-29",
    "method": "GET",
    "params": {
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "resourcePath": $util.toJson("/v1/users/${ctx.args.id}")
}
```
+ In **Configure the response mapping template (Configura il modello di mappatura della risposta)**, incollare il codice seguente:

```
## return the body
#if($ctx.result.statusCode == 200)
    ##if response is 200
    $ctx.result.body
#else
    ##if response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Scegliere la scheda **Query** quindi eseguire la seguente query:

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

Viene restituita la risposta seguente:

```
{
    "data": {
        "getUser": {
            "id": "1",
            "username": "nadia"
        }
    }
}
```
+ Scegli la scheda **Schema**.
+ Nel riquadro **Data types (Tipi di dati)** a destra sotto **Mutation (Mutazione)**, individuare il campo **addUser** e scegliere **Attach (Collega)**.
+ In **Data source name (Nome origine dati)**, scegliere **HTTP**.
+ In **Configure the request mapping template (Configura il modello di mappatura della richiesta)**, incollare il codice seguente:

```
{
    "version": "2018-05-29",
    "method": "POST",
    "resourcePath": "/v1/users",
    "params":{
      "headers":{
        "Content-Type": "application/json",
      },
      "body": $util.toJson($ctx.args.userInput)
    }
}
```
+ In **Configure the response mapping template (Configura il modello di mappatura della risposta)**, incollare il codice seguente:

```
## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
    $util.error($ctx.error.message, $ctx.error.type)
#end
## if the response status code is not 200, then return an error. Else return the body **
#if($ctx.result.statusCode == 200)
    ## If response is 200, return the body.
    $ctx.result.body
#else
    ## If response is not 200, append the response to error block.
    $utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
```
+ Scegliere la scheda **Query** quindi eseguire la seguente query:

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

Viene restituita la risposta seguente:

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

## Richiamo dei servizi AWS
<a name="invoking-aws-services"></a>

È possibile utilizzare i resolver HTTP per configurare un'interfaccia API GraphQL per i servizi. AWS Le richieste HTTP AWS devono essere firmate con il [processo Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) in modo da AWS poter identificare chi le ha inviate. AWS AppSync calcola la firma per tuo conto quando associ un ruolo IAM all'origine dati HTTP.

Fornisci due componenti aggiuntivi per richiamare i AWS servizi con resolver HTTP:
+ Un ruolo IAM con autorizzazioni per chiamare il servizio AWS APIs
+ Configurazione della firma nell'origine dati

Ad esempio, se desideri chiamare l'[ListGraphqlApis operazione](https://docs.aws.amazon.com/appsync/latest/APIReference/API_ListGraphqlApis.html) con resolver HTTP, devi prima [creare un ruolo IAM](attaching-a-data-source.md#aws-appsync-getting-started-build-a-schema-from-scratch) che AWS AppSync presuppone la seguente policy allegata:

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

****  

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

------

Quindi, crea l'origine dati HTTP per. AWS AppSync In questo esempio, chiami AWS AppSync nella regione Stati Uniti occidentali (Oregon). Imposta la seguente configurazione HTTP in un file denominato `http.json`, che include la regione di firma e il nome del servizio:

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

Quindi, utilizzate il AWS CLI per creare l'origine dati con un ruolo associato come segue:

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

Quando colleghi un resolver al campo dello schema, usa il seguente modello di mappatura delle richieste per chiamare: AWS AppSync

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

Quando esegui una query GraphQL per questa fonte di dati, AWS AppSync firma la richiesta utilizzando il ruolo che hai fornito e include la firma nella richiesta. La query restituisce un elenco di AWS AppSync GraphQL APIs nel tuo account in quella AWS regione.

# Utilizzo di Aurora Serverless v2 con AWS AppSync
<a name="tutorial-rds-resolvers"></a>

Connect l'API GraphQL ai database Aurora Serverless utilizzando. AWS AppSync Questa integrazione consente di eseguire istruzioni SQL tramite query, mutazioni e sottoscrizioni GraphQL, offrendoti un modo flessibile di interagire con i tuoi dati relazionali.

**Nota**  
In questo tutorial viene utilizzata la regione `US-EAST-1`.

**Vantaggi**
+ Perfetta integrazione tra GraphQL e database relazionali
+ Capacità di eseguire operazioni SQL tramite interfacce GraphQL
+ Scalabilità senza server con Aurora Serverless v2
+ Accesso sicuro ai dati tramite AWS Secrets Manager
+ Protezione contro l'SQL injection tramite la sanificazione degli input
+ Funzionalità di interrogazione flessibili, tra cui operazioni di filtraggio e intervallo

**Casi di utilizzo comune**
+ Creazione di applicazioni scalabili con requisiti di dati relazionali
+ La creazione di APIs ciò richiede sia la flessibilità GraphQL che le funzionalità del database SQL
+ Gestione delle operazioni sui dati tramite mutazioni e query GraphQL
+ Implementazione di modelli di accesso sicuri al database

In questo tutorial, imparerai quanto segue.
+ Configura un cluster Aurora Serverless v2
+ Abilita la funzionalità Data API
+ Crea e configura strutture di database
+ Definizione degli schemi GraphQL per le operazioni del database
+ Implementa resolver per query e mutazioni
+ Proteggi l'accesso ai dati mediante un'adeguata sanificazione degli input
+ Esegui varie operazioni di database tramite interfacce GraphQL

**Topics**
+ [Configurazione del cluster di database](#create-cluster)
+ [Abilitazione dell'API di dati](#enable-data-api)
+ [Creazione di un database e di una tabella](#create-database-and-table)
+ [Schema GraphQL](#graphql-schema)
+ [Connect la tua API alle operazioni del database](#configuring-resolvers)
+ [Modifica i tuoi dati tramite l'API](#run-mutations)
+ [Recupera i tuoi dati](#run-queries)
+ [Proteggi l'accesso ai dati](#input-sanitization)

## Configurazione del cluster di database
<a name="create-cluster"></a>

Prima di aggiungere un'origine dati Amazon RDS a AWS AppSync, devi prima abilitare una Data API su un cluster Aurora Serverless v2 **e** configurare un utilizzo segreto. *Gestione dei segreti AWS* È possibile creare un cluster Aurora Serverless v2 utilizzando: AWS CLI

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

Verrà restituito un ARN per il cluster.

Dopo aver creato il cluster, è necessario aggiungere un'istanza Aurora Serverless v2 utilizzando il comando seguente.

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

**Nota**  
L'attivazione di questi endpoint richiede tempo. Puoi verificarne lo stato nella console Amazon RDS nella scheda **Connettività e sicurezza** del cluster. Puoi anche controllare lo stato del tuo cluster con il seguente AWS CLI comando.   

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

È possibile creare un *Secret* utilizzando la Gestione dei segreti AWS Console o AWS CLI con un file di input come il seguente utilizzando la `USERNAME` e `COMPLEX_PASSWORD` del passaggio precedente.

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

Passalo come parametro a AWS CLI:

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

Verrà restituito un ARN per il segreto.

 **Prendi nota dell'ARN** del cluster Aurora Serverless e di Secret per utilizzarli successivamente nella AppSync console durante la creazione di un'origine dati.

## Abilitazione dell'API di dati
<a name="enable-data-api"></a>

È possibile abilitare l'API di dati sul cluster [seguendo le istruzioni nella documentazione di RDS](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html). L'API Data deve essere abilitata prima di aggiungerla come fonte di dati. AppSync 

## Creazione di un database e di una tabella
<a name="create-database-and-table"></a>

Dopo aver abilitato la Data API, puoi assicurarti che funzioni con il `aws rds-data execute-statement` comando contenuto in AWS CLI. Ciò garantirà che il cluster Aurora Serverless sia configurato correttamente prima di aggiungerlo all'API. AppSync Innanzitutto, creare un database denominato *TESTDB* con il parametro `--sql`, in questo modo:

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

Se la creazione viene eseguita senza errori, aggiungere una tabella con il comando *create table*:

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

Se tutto è stato eseguito senza problemi, puoi passare all'aggiunta del cluster come fonte di dati AppSync nell'API.

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

Ora che l'API di Aurora Serverless è operativa e dispone di una tabella, creeremo uno schema GraphQL e collegheremo i resolver per l'esecuzione di mutazioni e sottoscrizioni. Crea una nuova API nella AWS AppSync console, vai alla pagina **Schema** e inserisci quanto segue:

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

input CreatePetInput {
    type: PetType
    price: Float!
}

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

input DeletePetInput {
    id: ID!
}

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

enum PetType {
    dog
    cat
    fish
    bird
    gecko
}

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

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

 Salvare lo schema con **Save (Salva)**, accedere alla pagina **Data Sources (Origini dati)** e creare una nuova origine dati. Selezionare **Relational database (Database relazionale)** come tipo di origine dati e fornire un nome intellegibile. Utilizzare il nome del database creato nell'ultima fase e l'**ARN del cluster** in cui tale nome è stato creato. Per il **ruolo** puoi AppSync creare un nuovo ruolo o crearne uno con una politica simile alla seguente:

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

****  

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

------

Esistono due **istruzioni** in questa policy a cui viene concesso l'accesso basato sul ruolo. La prima **risorsa** è il cluster Aurora Serverless e la seconda è l'ARN. Gestione dei segreti AWS **Dovrai fornire **ENTRAMBI** ARNs nella configurazione dell'origine AppSync dati prima di fare clic su Crea.**

Passalo come parametro a AWS CLI.

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

Verrà restituito un ARN per il segreto. Prendi nota dell'ARN del tuo cluster Aurora Serverless e di Secret per dopo quando crei un'origine dati nella console. AWS AppSync 

### Costruisci la struttura del tuo database
<a name="create-database-and-table"></a>

Dopo aver abilitato la tua Data API, puoi assicurarti che funzioni con il `aws rds-data execute-statement` comando contenuto in AWS CLI. Ciò garantirà che il cluster Aurora Serverless v2 sia configurato correttamente prima di aggiungerlo all'API. AWS AppSync Innanzitutto, crea un database chiamato *TESTDB* con il `--sql` parametro seguente.

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

Se viene eseguito senza errori, aggiungete una tabella con il seguente comando *create table*.

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

### Progetta la tua interfaccia API
<a name="graphql-schema"></a>

Dopo che Aurora Serverless v2 Data API è attiva e funzionante con una tabella, crea uno schema GraphQL e collega i resolver per eseguire mutazioni e sottoscrizioni. Crea una nuova API nella console, vai alla pagina **Schema** nella AWS AppSync console e inserisci quanto segue.

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

 Salvare lo schema con **Save (Salva)**, accedere alla pagina **Data Sources (Origini dati)** e creare una nuova origine dati. Scegli **Database relazionale** per il tipo di **origine dati** e fornisci un nome descrittivo. Utilizzare il nome del database creato nell'ultima fase e l'**ARN del cluster** in cui tale nome è stato creato. Per il **ruolo** puoi AWS AppSync creare un nuovo ruolo o crearne uno con una politica simile alla seguente.

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

****  

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

------

Esistono due **istruzioni** in questa policy a cui viene concesso l'accesso basato sul ruolo. La prima **risorsa** è il cluster Aurora Serverless v2 e la seconda è l'ARN. Gestione dei segreti AWS **Dovrai fornire **ENTRAMBI** ARNs nella configurazione dell'origine AWS AppSync dati prima di fare clic su Crea.**

## Connect la tua API alle operazioni del database
<a name="configuring-resolvers"></a>

Ora che abbiamo uno schema GraphQL valido e un'origine dati RDS, puoi collegare i resolver ai campi GraphQL dello schema. La nostra API offrirà le seguenti funzionalità:

1. *crea un animale domestico usando il campo Mutation.createPet*

1. *aggiorna un animale domestico usando il campo Mutation.updatePet*

1. *elimina un animale domestico usando il campo Mutation.deletePet*

1. *ottenine un singolo utilizzo tramite il campo query.getPET*

1. *elenca tutto usando il campo query.listPets*

1. *elenca gli animali domestici in una fascia di prezzo utilizzando il comando Query. listPetsByPriceRange*campo

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

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver for**. `createPet(input: CreatePetInput!): Pet` Scegliere l'origine dati RDS. Aggiungere il modello seguente nella sezione **request mapping template (modello di mappatura della richiesta)**:

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

**Il sistema esegue le istruzioni SQL in sequenza, in base all'ordine nell'array delle istruzioni.** I risultati verranno restituiti nello stesso ordine. Poiché si tratta di una mutazione, verrà eseguita un'istruzione *select* dopo l'*inserimento per recuperare i valori confermati* al fine di popolare il modello di mappatura delle risposte GraphQL.

Aggiungere il modello seguente nella sezione **response mapping template (modello di mappatura della risposta)**:

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

Poiché le *istruzioni* contengono due query SQL, è necessario specificare il secondo risultato nella matrice restituita dal database con: `$utils.rds.toJsonString($ctx.result))[1][0])`.

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

**Dall'editor dello schema nella AWS AppSync console, scegli Attach Resolver for.** `updatePet(input: UpdatePetInput!): Pet` Scegli la tua fonte di **dati RDS.** Nella sezione del **modello di mappatura della richiesta**, aggiungi il seguente modello.

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

Nella sezione **modello di mappatura delle risposte**, aggiungi il seguente modello.

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

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

Dall'editor dello schema nella AWS AppSync console, scegli **Attach Resolver** for. `deletePet(input: DeletePetInput!): Pet` Scegli la tua fonte di **dati RDS.** Nella sezione del **modello di mappatura della richiesta**, aggiungi il seguente modello.

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

Nella sezione **modello di mappatura delle risposte**, aggiungi il seguente modello.

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

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

Ora che le mutazioni sono state create per il tuo schema, collega le tre query per mostrare come ottenere singoli elementi, elenchi e applicare il filtro SQL. **Dall'**editor dello schema** nella AWS AppSync console, scegli Attach Resolver for.** `getPet(id: ID!): Pet` Scegli la tua fonte di **dati RDS.** Nella sezione del **modello di mappatura della richiesta**, aggiungi il seguente modello.

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

Aggiungere il modello seguente nella sezione **response mapping template (modello di mappatura della risposta)**:

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

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

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver** for. `getPet(id: ID!): Pet` Scegli la tua fonte di **dati RDS.** Nella sezione del **modello di mappatura della richiesta**, aggiungi il seguente modello.

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

Nella sezione **modello di mappatura delle risposte**, aggiungi il seguente modello.

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

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

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver for**. `getPet(id: ID!): Pet` Scegli la tua fonte di **dati RDS.** Nella sezione del **modello di mappatura della richiesta**, aggiungi il seguente modello.

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

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

Aggiungere il modello seguente nella sezione **response mapping template (modello di mappatura della risposta)**:

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

## Modifica i tuoi dati tramite l'API
<a name="run-mutations"></a>

Ora che sono stati configurati tutti i resolver con le istruzioni SQL e l'API GraphQL è stata collegata all'API di dati per Aurora Serverless, è possibile iniziare a eseguire mutazioni e query. Nella AWS AppSync console, scegli la scheda **Query** e inserisci quanto segue per creare un animale domestico:

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

La risposta dovrebbe contenere l'*id*, il *type (tipo)* e il *price (prezzo)*, come indicato di seguito:

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

È possibile modificare questo elemento eseguendo la mutazione *updatePet*:

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

Abbiamo utilizzato l'*id* che è stato restituito dall'operazione *createPet* precedente. Questo sarà un valore univoco per il record poiché il resolver si è basato su `$util.autoId()`. È possibile eliminare un record in modo analogo:

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

Creare di alcuni record con la prima mutazione con valori diversi per *price (prezzo)*, quindi eseguire alcune query.

## Recupera i tuoi dati
<a name="run-queries"></a>

Sempre nella scheda **Queries** della console, usa la seguente istruzione per elencare tutti i record che hai creato.

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

*Sfrutta il predicato SQL *WHERE* presente `where price > :MIN and price < :MAX` nel nostro modello di mappatura per Query. listPetsByPriceRange*con la seguente query GraphQL:

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

Si dovrebbero visualizzare solo i record con *price (prezzo)* superiore a \$11 o inferiore a \$110. Infine, è possibile eseguire le query per recuperare singoli record, nel modo seguente:

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

## Proteggi l'accesso ai dati
<a name="input-sanitization"></a>

L'iniezione SQL è una vulnerabilità di sicurezza nelle applicazioni di database. Si verifica quando gli aggressori inseriscono codice SQL dannoso attraverso i campi di input dell'utente. Ciò può consentire l'accesso non autorizzato ai dati del database. Si consiglia di convalidare e disinfettare attentamente tutti gli input dell'utente prima dell'elaborazione, utilizzandoli `variableMap` per la protezione dagli attacchi di SQL injection. Se non vengono utilizzate mappe variabili, è responsabilità dell'utente sanificare gli argomenti delle loro operazioni GraphQL. Un possibile modo è fornire fasi di convalida specifiche dell'input nel modello di mappatura della richiesta prima dell'esecuzione di un'istruzione SQL sull'API di dati. Vediamo come possiamo modificare il modello di mappatura della richiesta dell'esempio `listPetsByPriceRange`. Anziché basarsi esclusivamente sull'input dell'utente, è possibile procedere nel modo seguente:

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

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


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

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

Un altro modo per proteggersi da input anomali durante l'esecuzione di resolver sull'API di dati consiste nell'utilizzare istruzioni preparate assieme a procedure memorizzate e input parametrici. Ad esempio, nel resolver per `listPets`, definire la procedura seguente che esegue il *select* come istruzione preparata:

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

Crealo nella tua istanza Aurora Serverless v2.

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

Il codice del resolver risultante per listPets è semplificato poiché ora è sufficiente chiamare la procedura memorizzata. Come minimo, qualsiasi input di stringa deve avere le virgolette singole tra caratteri di [escape](#escaped).

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

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

### Utilizzo delle stringhe di escape
<a name="escaped"></a>

Usa virgolette singole per contrassegnare l'inizio e la fine delle stringhe letterali in un'istruzione SQL, ad esempio.. `'some string value'`. Per consentire l'utilizzo di valori stringa con uno o più caratteri virgolette singole (`'`) all'interno di una stringa, ciascuna virgoletta deve essere sostituita con due virgolette singole (`''`). Ad esempio, se la stringa di input è `Nadia's dog`, inserisci il carattere di escape per l'istruzione SQL come segue

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

# Utilizzo di risolutori di pipeline in AWS AppSync
<a name="tutorial-pipeline-resolvers"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

AWS AppSync fornisce un modo semplice per collegare un campo GraphQL a una singola fonte di dati tramite resolver di unità. Tuttavia, un'unica operazione potrebbe non essere sufficiente. I resolver della pipeline offrono la possibilità di eseguire operazioni seriali sulle origini dati. Creazione di funzioni nell'API e collegamento a un resolver della pipeline. Il risultato di ogni funzione eseguita viene reindirizzato alla funzione successiva finché non rimangono altre funzioni da eseguire. Con i resolver della pipeline, è ora possibile creare flussi di lavoro più complessi direttamente in AWS AppSync. In questo tutorial, verrà creata una semplice applicazione per la visualizzazione di immagini, in cui gli utenti possono pubblicare immagini e visualizzare quelle pubblicate dai loro amici.

## Impostazione One-Click
<a name="one-click-setup"></a>

Se desideri configurare automaticamente l'endpoint GraphQL AWS AppSync con tutti i resolver configurati e le AWS risorse necessarie, puoi utilizzare il seguente modello: AWS CloudFormation 

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

Con questo stack. nell'account vengono create le seguenti risorse:
+ IAM Role per accedere AWS AppSync alle risorse del tuo account
+ 2 tabelle DynamoDB
+ 1 pool di utenti di Amazon Cognito
+ 2 gruppi di pool di utenti di Amazon Cognito
+ 3 utenti di pool di utenti di Amazon Cognito
+ 1 API AWS AppSync 

Al termine del processo di creazione dello AWS CloudFormation stack, riceverai un'e-mail per ciascuno dei tre utenti Amazon Cognito creati. Ogni e-mail contiene una password temporanea che usi per accedere come utente Amazon Cognito alla AWS AppSync console. Salvare le password per la parte restante del tutorial.

## Configurazione manuale
<a name="manual-setup"></a>

Se preferisci eseguire manualmente una step-by-step procedura tramite la AWS AppSync console, segui la procedura di configurazione riportata di seguito.

### Configurazione AWS AppSync delle risorse non aziendali
<a name="setting-up-your-non-aws-appsync-resources"></a>

L'API comunica con due tabelle DynamoDB: **una tabella di immagini che memorizza le immagini e **una**** tabella di amici che memorizza le relazioni tra gli utenti. L'API è configurata per utilizzare un pool di utenti di Amazon Cognito come tipo di autenticazione. Lo CloudFormation stack seguente configura queste risorse nell'account.

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

Al termine del processo di creazione dello AWS CloudFormation stack, riceverai un'e-mail per ciascuno dei tre utenti Amazon Cognito creati. Ogni e-mail conterrà una password temporanea utilizzabile per effettuare l'accesso come utente Amazon Cognito alla console di AWS AppSync. Salvare le password per la parte restante del tutorial.

### Creazione dell'API GraphQL
<a name="creating-your-graphql-api"></a>

Per creare l'API GraphQL in: AWS AppSync

1. Apri la AWS AppSync console e scegli **Build From Scratch** e scegli **Start**.

1. Impostare il nome dell'API su `AppSyncTutorial-PicturesViewer`.

1. Scegli **Create** (Crea).

La AWS AppSync console crea una nuova API GraphQL utilizzando la modalità di autenticazione della chiave API. Puoi usare la console per configurare ulteriormente l'API GraphQL ed eseguire query sull'API per le parti restanti di questo tutorial.

### Configurazione dell'API per GraphQL
<a name="configuring-the-graphql-api"></a>

Devi configurare l' AWS AppSync API con il pool di utenti Amazon Cognito che hai appena creato.

1. Seleziona la scheda **Impostazioni**.

1. Nella sezione **Authorization Type (Tipo di autorizzazione)**, scegliere *Amazon Cognito User Pool (Pool di utenti di Amazon Cognito)*.

1. *In **Configurazione del pool di utenti**, scegli **US-WEST-2 per la regione**.AWS *

1. Scegli il pool di UserPool utenti **AppSyncTutorial-**.

1. Scegliere **DENY (NEGA)** come *Default Action (Operazione predefinita)*.

1. Lascia vuoto il campo **regex del AppId client**.

1. Scegli **Save** (Salva).

L'API è ora configurata per utilizzare un pool di utenti di Amazon Cognito come tipo di autorizzazione.

### Configurazione delle sorgenti dati per le tabelle DynamoDB
<a name="configuring-data-sources-for-the-ddb-tables"></a>

**Dopo aver creato le tabelle DynamoDB, accedi all'API AWS AppSync GraphQL nella console e scegli la scheda Data Sources.** Ora creerai un'origine dati AWS AppSync per ciascuna delle tabelle DynamoDB che hai appena creato.

1. Passa alla scheda **Data source (Origine dati)**.

1. Scegli **New (Nuovo)** per creare una nuova origine dati.

1. Per il nome dell'origine dati, immetti `PicturesDynamoDBTable`.

1. Scegli **Amazon DynamoDB table (Tabella Amazon DynamoDB)** come tipo di origine dati.

1. Scegli **US-WEST-2** per la regione.

1. Dall'elenco delle tabelle, scegli la tabella **AppSyncTutorial-Pictures** DynamoDB.

1. Scegliere **Existing role (Ruolo esistente)** nella sezione **Create or use an existing role (Crea o usa un ruolo esistente)**.

1. Scegli il ruolo appena creato dal modello. CloudFormation Se non hai cambiato il *ResourceNamePrefix*, il nome del ruolo dovrebbe essere **AppSyncTutorial-Dynamo DBRole**.

1. Scegli **Create** (Crea).

Ripeti la stessa procedura per la tabella degli **amici**, il nome della tabella DynamoDB dovrebbe **AppSyncTutorialessere** -Friends se non hai modificato *ResourceNamePrefix*il parametro al momento della creazione dello stack. CloudFormation 

### Creazione dello schema GraphQL
<a name="creating-the-graphql-schema"></a>

Ora che le sorgenti dati sono collegate alle tabelle DynamoDB, creiamo uno schema GraphQL. Dall'editor di schemi nella AWS AppSync console, assicurati che lo schema corrisponda al seguente schema:

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

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

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

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

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

Scegliere **Save Schema (Salva schema)** per salvare lo schema.

Alcuni campi dello schema sono stati annotati con la direttiva *@aws\$1auth*. Poiché l'operazione predefinita dell'API è impostata su *DENY (NEGA)*, l'API rifiuta tutti gli utenti che non sono membri dei gruppi inclusi nella direttiva *@aws\$1auth*. Per ulteriori informazioni su come proteggere l'API, è possibile leggere la pagina sulla [sicurezza](security-authz.md#aws-appsync-security). **In questo caso, solo gli utenti amministratori hanno accesso ai campi *Mutation.createPicture e *Mutation.createFriendship**, mentre gli utenti membri dei gruppi Admins o Viewers possono accedere alla Query.** * getPicturesByCampo Proprietario.* Tutti gli altri utenti non hanno accesso.

### Configurazione dei resolver
<a name="configuring-resolvers"></a>

Ora che si dispone di un valido schema GraphQL e di due origini dati, è possibile collegare i resolver ai campi GraphQL dello schema. L'API offre le seguenti funzionalità:
+ Creazione di un'immagine tramite il campo *Mutation.createPicture*
+ Creazione di una richiesta di amicizia tramite il campo *Mutation.createFriendship*
+ Recupero di un'immagine tramite il campo *Query.getPicture*

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

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver for**. `createPicture(input: CreatePictureInput!): Picture!` Scegli l'origine dati *PicturesDynamoDBTable*DynamoDB. Aggiungere il modello seguente nella sezione **request mapping template (modello di mappatura della richiesta)**:

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

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

    "operation" : "PutItem",

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

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

Aggiungere il modello seguente nella sezione **response mapping template (modello di mappatura della risposta)**:

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

La funzionalità per la creazione di immagini è stata creata. Verrà salvata un'immagine nella tabella **pictures** con un UUID casuale come id dell'immagine e il nome utente di Cognito come proprietario dell'immagine.

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

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver** for. `createFriendship(id: ID!, target: ID!): Boolean` Scegli l'origine dati **FriendsDynamoDBTable**DynamoDB. Aggiungere il modello seguente nella sezione **request mapping template (modello di mappatura della richiesta)**:

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

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        ## Replace 'AppSyncTutorial-' default below with the ResourceNamePrefix you provided in the CloudFormation template
        "AppSyncTutorial-Friends": $util.toJson($friendsItems)
    }
}
```

Importante: nel modello di **BatchPutItem**richiesta, deve essere presente il nome esatto della tabella DynamoDB. *Il nome di tabella predefinito è AppSyncTutorial -Friends.* Se si utilizza il nome di tabella sbagliato, viene visualizzato un errore quando si AppSync tenta di assumere il ruolo fornito.

Per semplicità, in questo tutorial, procedi come se la richiesta di amicizia fosse stata approvata e salva la voce della relazione direttamente nella **AppSyncTutorialFriends**tabella.

Di fatto, si stanno memorizzando due voci per ogni richiesta di amicizia dato che la relazione è bidirezionale. Per ulteriori dettagli sulle best practice di Amazon DynamoDB per many-to-many rappresentare le relazioni, consulta [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html) Best Practices.

Aggiungere il modello seguente nella sezione **response mapping template (modello di mappatura della risposta)**:

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

Nota: verificare che il proprio modello di richiesta contenga il nome corretto della tabella. Il nome predefinito è *AppSyncTutorial-Friends*, ma il nome della tabella potrebbe essere diverso se modifichi il parametro. CloudFormation **ResourceNamePrefix**

#### Interrogazione. getPicturesByProprietario
<a name="query-getpicturesbyowner"></a>

Ora che si dispone di richieste di amicizia e immagini, è necessario fornire la possibilità agli utenti di visualizzare le immagini dei loro amici. Per soddisfare questo requisito, occorre prima verificare che il richiedente sia amico del proprietario, quindi è necessario eseguire una query per ottenere l'immagine.

Poiché questa funzionalità richiede due operazioni sull'origine dati, si creano due funzioni. La prima funzione, **isFriend**, verifica se il richiedente e il proprietario sono amici. La seconda funzione, **getPicturesByOwner**, recupera le immagini richieste con un ID proprietario. *Diamo un'occhiata al flusso di esecuzione riportato di seguito per il resolver proposto sulla Query. getPicturesByCampo proprietario*:

1. Modello di mappatura Before: prepara il contesto e gli argomenti di input per il campo.

1. Funzione isFriend: verifica se il richiedente è il proprietario dell'immagine. In caso contrario, verifica se gli utenti richiedenti e proprietari sono amici eseguendo un'operazione GetItem DynamoDB sulla tabella degli amici.

1. getPicturesBy*Funzione Owner: recupera le immagini dalla tabella Pictures utilizzando un'operazione di interrogazione DynamoDB sull'indice secondario globale owner-index.*

1. Modello di mappatura After: mappa le immagini risultanti in modo che gli attributi di DynamoDB siano mappati correttamente con i campi dei tipi GraphQL previsti.

Innanzitutto, verranno create le funzioni.

##### Funzione isFriend
<a name="isfriend-function"></a>

1. Selezionare la scheda **Functions (Funzioni)**.

1. Scegliere **Create Function (Crea funzione)** per creare una funzione.

1. Per il nome dell'origine dati, immetti `FriendsDynamoDBTable`.

1. Come nome della funzione, immettere *isFriend*.

1. Nell'area di testo del modello di mappatura della richiesta, incollare il seguente modello:

   ```
   #set($ownerId = $ctx.prev.result.owner)
   #set($callerId = $ctx.prev.result.callerId)
   
   ## if the owner is the caller, no need to make the check
   #if($ownerId == $callerId)
       #return($ctx.prev.result)
   #end
   
   {
       "version" : "2018-05-29",
   
       "operation" : "GetItem",
   
       "key" : {
           "userId" : $util.dynamodb.toDynamoDBJson($callerId),
           "friendId" : $util.dynamodb.toDynamoDBJson($ownerId)
       }
   }
   ```

1. Nell'area di testo del modello di mappatura della risposta, incollare il seguente modello:

   ```
   #if($ctx.error)
       $util.error("Unable to retrieve friend mapping message: ${ctx.error.message}", $ctx.error.type)
   #end
   
   ## if the users aren't friends
   #if(!$ctx.result)
       $util.unauthorized()
   #end
   
   $util.toJson($ctx.prev.result)
   ```

1. Selezionare **Create function (Crea funzione)**.

Risultato: è stata creata la funzione **isFriend**.

##### getPicturesByFunzione del proprietario
<a name="getpicturesbyowner-function"></a>

1. Selezionare la scheda **Functions (Funzioni)**.

1. Scegliere **Create Function (Crea funzione)** per creare una funzione.

1. Per il nome dell'origine dati, immetti `PicturesDynamoDBTable`.

1. Come nome della funzione, immettere `getPicturesByOwner`.

1. Nell'area di testo del modello di mappatura della richiesta, incollare il seguente modello:

   ```
   {
       "version" : "2018-05-29",
   
       "operation" : "Query",
   
       "query" : {
           "expression": "#owner = :owner",
           "expressionNames": {
               "#owner" : "owner"
           },
           "expressionValues" : {
               ":owner" : $util.dynamodb.toDynamoDBJson($ctx.prev.result.owner)
           }
       },
   
       "index": "owner-index"
   }
   ```

1. Nell'area di testo del modello di mappatura della risposta, incollare il seguente modello:

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

1. Selezionare **Create function (Crea funzione)**.

Risultato: hai creato la funzione **getPicturesByOwner**. *Ora che le funzioni sono state create, collegate un resolver di pipeline alla Query. getPicturesByCampo Proprietario.*

Dall'editor dello schema nella AWS AppSync console, sul lato destro scegli **Attach Resolver for**. `Query.getPicturesByOwner(id: ID!): [Picture]` Nella pagina seguente, selezionare il collegamento **Convert to pipeline resolver (Converti in resolver della pipeline)** che viene visualizzato sotto l'origine dati nell'elenco a discesa. Utilizzare la procedura seguente per il modello di mappatura Before:

```
#set($result = { "owner": $ctx.args.id, "callerId": $ctx.identity.username })
$util.toJson($result)
```

Nella sezione **after mapping template (modello di mappatura After)**, utilizzare la procedura seguente:

```
#foreach($picture in $ctx.result.items)
    ## prepend "src://" to picture.src property
    #set($picture['src'] = "src://${picture['src']}")
#end
$util.toJson($ctx.result.items)
```

Scegliere **Create Resolver (Crea resolver)**. Il primo resolver della pipeline è stato ora collegato. Nella stessa pagina, aggiungere le due funzioni create in precedenza. Nella sezione delle funzioni, scegliere **Add A Function (Aggiungi funzione)**, quindi scegliere o digitare il nome della prima funzione, **isFriend**. Aggiungi la seconda funzione seguendo la stessa procedura per la funzione **getPicturesByOwner**. Assicuratevi che la funzione **isFriend** compaia per prima nell'elenco seguita dalla funzione **getPicturesByOwner**. È possibile utilizzare le frecce Su è Giù per riorganizzare le funzioni per ordinarne l'esecuzione nella pipeline.

Una volta creato il resolver della pipeline e collegate le funzioni, è opportuno testare la nuova API GraphQL.

## Test dell'API GraphQL
<a name="testing-your-graphql-api"></a>

Innanzitutto, occorre inserire immagini e richieste di amicizia eseguendo alcune mutazioni con l'utente con privilegi di amministratore creato. Sul lato sinistro della AWS AppSync console, scegli la scheda **Interrogazioni**.

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

1. Nella AWS AppSync console, scegli la scheda **Interrogazioni.**

1. Scegliere **Login With User Pools (Accedi con pool di utenti)**.

1. Nella modalità modale, inserisci l'ID client Cognito Sample creato dallo stack (ad esempio CloudFormation 37solo6mmhh7k4v63cqdfgdg5d).

1. Immettete il nome utente CloudFormation che avete passato come parametro allo stack. L'impostazione predefinita è **nadia**.

1. Utilizza la password temporanea che è stata inviata all'e-mail che hai fornito come parametro per lo CloudFormation stack (ad esempio, *UserPoolUserEmail*).

1. Selezionare **Login (Accesso)**. Ora dovresti vedere il pulsante rinominato **Logout nadia**, o qualunque nome utente tu abbia scelto durante la creazione dello CloudFormation stack (ovvero,). *UserPoolUsername*

A questo punto trasmetteremo alcune mutazioni *createPicture* per popolare la tabella delle immagini. Eseguire la seguente query GraphQL nella console:

```
mutation {
  createPicture(input:{
    owner: "nadia"
    src: "nadia.jpg"
  }) {
    id
    owner
    src
  }
}
```

La risposta dovrebbe essere simile alla seguente:

```
{
  "data": {
    "createPicture": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "owner": "nadia",
      "src": "nadia.jpg"
    }
  }
}
```

Aggiungiamo alcune altre immagini:

```
mutation {
  createPicture(input:{
    owner: "shaggy"
    src: "shaggy.jpg"
  }) {
    id
    owner
    src
  }
}
```

```
mutation {
  createPicture(input:{
    owner: "rex"
    src: "rex.jpg"
  }) {
    id
    owner
    src
  }
}
```

Sono state aggiunte tre immagini utilizzando **nadia** come utente con privilegi di amministratore.

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

Ora aggiungiamo una richiesta di amicizia. Eseguire le seguenti mutazioni nella console.

Nota: è necessario essere connessi con l'utente con privilegi di amministratore (l'utente predefinito è **nadia**).

```
mutation {
  createFriendship(id: "nadia", target: "shaggy")
}
```

La risposta dovrebbe essere simile alla seguente:

```
{
  "data": {
    "createFriendship": true
  }
}
```

 **nadia** e **shaggy** sono amici. **rex** non è amico di altri utenti.

### getPicturesByRichiesta del proprietario
<a name="getpicturesbyowner-query"></a>

Per questa fase, effettuare l'accesso con l'utente **nadia**, utilizzando pool di utenti di Cognito e le credenziali impostate all'inizio di questo tutorial. Come utente **nadia**, recuperare le immagini di proprietà di **shaggy**.

```
query {
    getPicturesByOwner(id: "shaggy") {
        id
        owner
        src
    }
}
```

Poiché **nadia** e **shaggy** sono amici, la query dovrebbe restituire l'immagine corrispondente.

```
{
  "data": {
    "getPicturesByOwner": [
      {
        "id": "05a16fba-cc29-41ee-a8d5-4e791f4f1079",
        "owner": "shaggy",
        "src": "src://shaggy.jpg"
      }
    ]
  }
}
```

Analogamente, anche un eventuale tentativo di **nadia** di recuperare proprie immagini andrebbe a buon fine. Il pipeline resolver è stato ottimizzato per evitare l'esecuzione dell'operazione ** GetItem isFriend** in questo caso. Provare la seguente query:

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Se si attiva la registrazione per le API (nel riquadro **Settings (Impostazioni))**, impostare il livello di debug a **ALL** ed eseguire di nuovo la stessa query; verranno restituiti i log per l'esecuzione effettiva. Esaminando i log, è possibile stabilire se la funzione **isFriend** ha restituito un output nelle prime fasi del **modello di mappatura della richiesta**:

```
{
  "errors": [],
  "mappingTemplateType": "Request Mapping",
  "path": "[getPicturesByOwner]",
  "resolverArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/types/Query/fields/getPicturesByOwner",
  "functionArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/functions/o2f42p2jrfdl3dw7s6xub2csdfs",
  "functionName": "isFriend",
  "earlyReturnedValue": {
    "owner": "nadia",
    "callerId": "nadia"
  },
  "context": {
    "arguments": {
      "id": "nadia"
    },
    "prev": {
      "result": {
        "owner": "nadia",
        "callerId": "nadia"
      }
    },
    "stash": {},
    "outErrors": []
  },
  "fieldInError": false
}
```

*La *earlyReturnedValue*chiave rappresenta i dati restituiti dalla direttiva \$1return.*

****Infine, anche se **rex** è membro del **Viewers Cognito** UserPool Group e poiché **rex** non è amico di nessuno, non potrà accedere a nessuna delle immagini di proprietà di Shaggy o Nadia.**** Se si effettua l'accesso come **rex** nella console e si esegue la query seguente:

```
query {
    getPicturesByOwner(id: "nadia") {
        id
        owner
        src
    }
}
```

Si otterrà il seguente errore per mancanza di autorizzazioni:

```
{
  "data": {
    "getPicturesByOwner": null
  },
  "errors": [
    {
      "path": [
        "getPicturesByOwner"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 9,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access getPicturesByOwner on type Query"
    }
  ]
}
```

L'autorizzazione complessa è stata correttamente implementata utilizzando i resolver della pipeline.

# Utilizzo delle operazioni Delta Sync su sorgenti dati con versione in AWS AppSync
<a name="tutorial-delta-sync"></a>

**Nota**  
Ora supportiamo principalmente il runtime APPSYNC\$1JS e la relativa documentazione. [Prendi in considerazione l'utilizzo del runtime APPSYNC\$1JS e delle relative guide qui.](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)

Le applicazioni client AWS AppSync archiviano i dati memorizzando nella cache le risposte GraphQL localmente sul disco di un'applicazione. mobile/web Le origini dati e le operazioni `Sync` con versioni offrono ai clienti la possibilità di eseguire il processo di sincronizzazione utilizzando un unico resolver. In tal modo, per i client è possibile rigenerare la cache locale con i risultati della query di base, dalla notevole quantità di record, per poi acquisire solo i dati variati dall'ultima query (gli *aggiornamenti differenziali*). Consentendo ai client di eseguire la rigenerazione di base della cache con una richiesta iniziale e con aggiornamenti incrementali con una seconda richiesta, è possibile trasferire le risorse e le operazioni di calcolo dall'applicazione client al back-end. Ciò è sostanzialmente più efficiente per le applicazioni client che spesso passano da uno stato online a uno stato offline.

Per implementare Delta Sync, la query `Sync` utilizza l'operazione `Sync` su un'origine dati con versione. Quando una AWS AppSync mutazione modifica un elemento in un'origine dati con versione, un record di tale modifica verrà archiviato anche nella tabella *Delta*. Puoi scegliere di utilizzare diverse tabelle *Delta* (ad esempio una per tipo, una per area di dominio) per altre fonti di dati con versione o una singola tabella *Delta* per la tua API. AWS AppSync sconsiglia di utilizzare una singola tabella *Delta* per più tabelle per APIs evitare la collisione delle chiavi primarie.

Inoltre, i client che si avvalgono della sincronizzazione differenziale possono ricevere una sottoscrizione sotto forma di argomento, per poi coordinarne le operazioni di scrittura e riconnessione nelle transizioni tra offline e online. A tale scopo, la sincronizzazione automatica riprende automaticamente le sottoscrizioni, inclusa una strategia di "backoff esponenziale e riprova con jitter" su più scenari di errore di rete e archivia gli eventi in una coda. Esegue poi, prima dell'unione degli eventi in coda, la query di base o differenziale appropriata e, infine, elabora le sottoscrizioni normalmente.

[La documentazione per le opzioni di configurazione del client, incluso Amplify, è disponibile sul sito Web di DataStore Amplify Framework.](https://aws-amplify.github.io/) Questa documentazione illustra come impostare origini dati e operazioni `Sync` DynamoDB con versioni per lavorare con il client Delta Sync per un accesso ottimale ai dati.

## Impostazione One-Click
<a name="one-click-setup"></a>

Per configurare automaticamente l'endpoint GraphQL AWS AppSync con tutti i resolver configurati e le risorse necessarie AWS , usa questo modello: AWS CloudFormation 

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

Con questo stack. nell'account vengono create le seguenti risorse:
+ 2 tabelle DynamoDB (Base e Delta)
+ 1 AWS AppSync API con chiave API
+ 1 ruolo IAM con policy per le tabelle DynamoDB

Due tabelle tornano utili per partizionare le query di sincronizzazione differenziali in una seconda tabella che funge da registro degli eventi non rilevati con i client offline. Per mantenere efficienti le query sulla tabella delta, [ TTLsAmazon](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) DynamoDB viene utilizzato per organizzare automaticamente gli eventi in base alle necessità. L'ora TTL è configurabile per le tue esigenze sull'origine dati (puoi impostarla come 1 ora, 1 giorno ecc.).

## Schema
<a name="schema"></a>

Per dimostrare Delta Sync, l'applicazione di esempio crea uno schema *Posts* supportato da una tabella *Base* e *Delta* in DynamoDB. AWS AppSync scrive automaticamente le mutazioni in entrambe le tabelle. Le query di sincronizzazione estraggono i record dalla tabella *Base* o *Delta*, in base alle esigenze, e viene definita un'unica sottoscrizione che mostra in che modo i client possono avvalersene nella propria logica di riconnessione.

```
input CreatePostInput {
    author: String!
    title: String!
    content: String!
    url: String
    ups: Int
    downs: Int
    _version: Int
}

interface Connection {
  nextToken: String
  startedAt: AWSTimestamp!
}

type Mutation {
    createPost(input: CreatePostInput!): Post
    updatePost(input: UpdatePostInput!): Post
    deletePost(input: DeletePostInput!): Post
}

type Post {
    id: ID!
    author: String!
    title: String!
    content: String!
    url: AWSURL
    ups: Int
    downs: Int
    _version: Int
    _deleted: Boolean
    _lastChangedAt: AWSTimestamp!
}

type PostConnection implements Connection {
    items: [Post!]!
    nextToken: String
    startedAt: AWSTimestamp!
}

type Query {
    getPost(id: ID!): Post
    syncPosts(limit: Int, nextToken: String, lastSync: AWSTimestamp): PostConnection!
}

type Subscription {
    onCreatePost: Post
        @aws_subscribe(mutations: ["createPost"])
    onUpdatePost: Post
        @aws_subscribe(mutations: ["updatePost"])
    onDeletePost: Post
        @aws_subscribe(mutations: ["deletePost"])
}

input DeletePostInput {
    id: ID!
    _version: Int!
}

input UpdatePostInput {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    _version: Int!
}

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

Lo schema di GraphQL è standard, tuttavia conviene soffermarsi su un paio di aspetti, prima di procedere. Innanzitutto, tutte le mutazioni scrivono automaticamente prima nella tabella *Base* e poi nella tabella *Delta*. La tabella *Base* è la fonte centrale di attendibilità per lo stato, mentre la *Differenziale* funge da registro. Se non si passa in `lastSync: AWSTimestamp`, la query `syncPosts` viene eseguita sulla tabella *Base* per rigenerare la cache e, periodicamente, come *procedura di aggiornamento globale*, per i casi limite in cui i client restano offline più a lungo rispetto alla tempistica di TTL configurata nella tabella *Delta*. Se si passa in `lastSync: AWSTimestamp`, la query `syncPosts` viene eseguita sulla tabella *Delta* e viene utilizzata dai client per recuperare gli eventi cambiati dall'ultimo passaggio offline. L'amplificazione dei client passa automaticamente il valore `lastSync: AWSTimestamp` e persiste sul disco in modo appropriato.

Il campo *\$1deleted* su *Post* viene utilizzato per operazioni **DELETE**. Quando i client sono offline e i record vengono rimossi dalla tabella *Base*, questo attributo notifica ai client l'eliminazione delle voci dalla cache locale in fase di sincronizzazione. Se i client restano offline per un periodo prolungato e la voce viene rimossa prima che si possa recuperare questo valore con una query di sincronizzazione differenziale, l'evento di aggiornamento globale nella query di base (configurabile nel client) viene eseguito e rimuove la voce dalla cache. Questo campo viene contrassegnato come facoltativo perché restituisce un valore solo all'esecuzione di una query di sincronizzazione responsabile dell'eliminazione di elementi.

## Mutazioni
<a name="mutations"></a>

Per tutte le mutazioni, AWS AppSync esegue un'Create/Update/Deleteoperazione standard nella tabella *Base* e registra automaticamente anche la modifica nella tabella *Delta*. È possibile ridurre o estendere il tempo di permanenza dei record, modificando il valore `DeltaSyncTableTTL` nell'origine dati. Alle organizzazioni che acquisiscono ed elaborano dati frequentemente e rapidamente si addice un periodo di permanenza ridotto. Se invece i client restano offline a lungo, è consigliabile impostare un periodo di permanenza prolungato.

## Query di sincronizzazione
<a name="sync-queries"></a>

La *query di base* è un'operazione di sincronizzazione DynamoDB senza `lastSync` un valore specificato. Lo standard torna utile a quelle organizzazioni che eseguono la query di base solo all'avvio e, successivamente, a cadenza periodica.

La *delta query* è un'operazione di sincronizzazione DynamoDB con `lastSync` un valore specificato. La *query delta* viene eseguita ogni volta che il client torna online da uno stato offline (se le tempistiche per le attività periodiche della query di base non hanno avviato l'esecuzione). I client monitorano automaticamente l'ultima volta che hanno eseguito correttamente una query per sincronizzare i dati.

Quando viene eseguita una query delta, il resolver della query utilizza `ds_pk` e `ds_sk` per eseguire query solo per i record che sono stati modificati dall'ultima volta che il client ha eseguito una sincronizzazione. Il client memorizza nella cache la risposta di GraphQL appropriata.

Per ulteriori informazioni sull'esecuzione di query di sincronizzazione, vedere la [documentazione relativa all'operazione di sincronizzazione](aws-appsync-conflict-detection-and-sync-sync-operations.md).

## Esempio
<a name="example"></a>

Iniziamo prima chiamando una mutazione `createPost` per creare un elemento:

```
mutation create {
  createPost(input: {author: "Nadia", title: "My First Post", content: "Hello World"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

Il valore di restituzione di questa mutazione sarà il seguente:

```
{
  "data": {
    "createPost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "My First Post",
      "content": "Hello World",
      "_version": 1,
      "_lastChangedAt": 1574469356331,
      "_deleted": null
    }
  }
}
```

Se si esamina il contenuto della tabella *Base* si vedrà un record simile a:

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Se si esamina il contenuto della tabella *Delta* si vedrà un record simile a:

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_ttl": {
    "N": "1574472956"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:35:56.331:81d36bbb-1579-4efe-92b8-2e3f679f628b:1"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

Ora possiamo simulare una query di *base* che un client eseguirà per rigenerare il suo archivio dati locale utilizzando una query `syncPosts` come:

```
query baseQuery {
  syncPosts(limit: 100, lastSync: null, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
      _lastChangedAt
    }
    startedAt
    nextToken
  }
}
```

Il valore restituito di questa query di *base* sarà il seguente:

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "My First Post",
          "content": "Hello World",
          "_version": 1,
          "_lastChangedAt": 1574469356331
        }
      ],
      "startedAt": 1574469602238,
      "nextToken": null
    }
  }
}
```

Salveremo il valore `startedAt` in seguito per simulare una query *Delta*, ma prima dobbiamo apportare una modifica alla nostra tabella. Usiamo la mutazione `updatePost` per modificare il nostro Post esistente:

```
mutation updatePost {
  updatePost(input: {id: "81d36bbb-1579-4efe-92b8-2e3f679f628b", _version: 1, title: "Actually this is my Second Post"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

Il valore di restituzione di questa mutazione sarà il seguente:

```
{
  "data": {
    "updatePost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "Actually this is my Second Post",
      "content": "Hello World",
      "_version": 2,
      "_lastChangedAt": 1574469851417,
      "_deleted": null
    }
  }
}
```

Se esamini il contenuto della tabella *Base* ora, dovresti vedere l'elemento aggiornato:

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Se esamini il contenuto della tabella *Delta* ora, dovresti vedere due record:

1. Un record al momento della creazione dell'elemento

1. Un record per quando l'elemento è stato aggiornato.

Il nuovo elemento sarà simile a:

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_ttl": {
    "N": "1574473451"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:44:11.417:81d36bbb-1579-4efe-92b8-2e3f679f628b:2"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

Ora possiamo simulare una query *Delta* per recuperare le modifiche che si sono verificate quando il client era offline. Useremo il valore `startedAt` restituito dalla nostra query di *base* per fare la richiesta:

```
query delta {
  syncPosts(limit: 100, lastSync: 1574469602238, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
    }
    startedAt
    nextToken
  }
}
```

Il valore restituito di questa query *Delta* sarà il seguente:

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "Actually this is my Second Post",
          "content": "Hello World",
          "_version": 2
        }
      ],
      "startedAt": 1574470400808,
      "nextToken": null
    }
  }
}
```