

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

# Utilizzo di Aurora PostgreSQL con Data API in AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Scopri come connettere l'API GraphQL ai database Aurora PostgreSQL utilizzando. AWS AppSync Questa integrazione consente di creare applicazioni scalabili e basate sui dati eseguendo query e mutazioni SQL tramite operazioni GraphQL. AWS AppSync fornisce un'origine dati per l'esecuzione di istruzioni SQL su cluster Amazon Aurora abilitati con un'API Data. Puoi utilizzare AWS AppSync i resolver per eseguire istruzioni SQL sull'API dei dati con query, mutazioni e sottoscrizioni GraphQL.

Prima di iniziare questo tutorial, è necessario avere una conoscenza di base AWS dei servizi e dei concetti di GraphQL.

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

**Topics**
+ [Configura il tuo database Aurora PostgreSQL](#creating-clusters)
+ [Creazione del database e della tabella](#creating-db-table)
+ [Creazione di uno schema GraphQL](#rds-graphql-schema)
+ [Resolver per RDS](#rds-resolvers)
+ [Eliminazione del cluster](#rds-delete-cluster)

## Configura il tuo database Aurora PostgreSQL
<a name="creating-clusters"></a>

Prima di aggiungere un'origine dati Amazon RDS AWS AppSync, procedi come segue.

1. Abilita un'API dati su un cluster Aurora Serverless v2.

1. Configura un segreto utilizzando Gestione dei segreti AWS

1. Crea il cluster utilizzando il seguente AWS CLI comando.

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

Verrà restituito un ARN per il cluster. Dopo aver creato un cluster, è necessario aggiungere un'istanza Serverless v2 con il comando seguente AWS CLI .

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

**Nota**  
L'attivazione di questi endpoint richiede tempo. Puoi controllarne lo stato nella console RDS nella scheda **Connettività e sicurezza** del cluster.

Controlla lo stato del cluster con il seguente AWS CLI comando.

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

Crea un segreto tramite la Gestione dei segreti AWS console o AWS CLI con un file di input come il seguente utilizzando i `USERNAME` e `COMPLEX_PASSWORD` del passaggio precedente:

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

Passalo come parametro a AWS CLI:

```
aws secretsmanager create-secret \
    --name appsync-tutorial-rds-secret \
    --secret-string file://creds.json
```

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

## Creazione del database e della tabella
<a name="creating-db-table"></a>

Innanzitutto, crea un database denominato`TESTDB`. In PostgreSQL, un database è un contenitore che contiene tabelle e altri oggetti SQL. Verifica che il cluster Aurora Serverless v2 sia configurato correttamente prima di aggiungerlo all'API. AWS AppSync Innanzitutto, crea un database *TESTDB* con il parametro seguente. `--sql`

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

 Se viene eseguito senza errori, aggiungete due tabelle con il `create table` comando:

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

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

In caso di successo, aggiungi il cluster come fonte di dati nella tua API.

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

Ora che la tua API dati Aurora Serverless v2 è in esecuzione con tabelle configurate, creeremo uno schema GraphQL. Puoi creare rapidamente la tua API importando le configurazioni delle tabelle da un database esistente utilizzando la procedura guidata di creazione dell'API.

Per iniziare: 

1. Nella AWS AppSync console, scegli **Crea API**, quindi **Inizia con un cluster Amazon Aurora**. 

1. Specificate i dettagli dell'**API, come il nome** dell'API, quindi selezionate il database per generare l'API.

1. Scegli il tuo database. Se necessario, aggiorna la regione, quindi scegli il cluster Aurora e il database *TESTDB*. 

1. **Scegli il tuo segreto, quindi scegli Importa.** 

1. Una volta scoperte le tabelle, aggiorna i nomi dei tipi. Passa `Todos` a `Todo` e `Tasks` a`Task`. 

1. Visualizza l'anteprima dello schema generato scegliendo **Anteprima schema**. Il tuo schema sarà simile al seguente: 

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

1. Per il ruolo, puoi AWS AppSync creare un nuovo ruolo o crearne uno con una politica simile a quella seguente:

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

****  

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

------

   Tieni presente che ci sono due dichiarazioni in questa politica a cui concedi l'accesso al ruolo. La prima risorsa è il cluster Aurora e la seconda è l' Gestione dei segreti AWS ARN. 

   Scegli **Avanti**, esamina i dettagli di configurazione, quindi scegli **Crea** API. Ora hai un'API completamente operativa. Puoi rivedere tutti i dettagli della tua API nella pagina **Schema**. 

## Resolver per RDS
<a name="rds-resolvers"></a>

Il flusso di creazione dell'API ha creato automaticamente i resolver per interagire con i nostri tipi. Se guardi la pagina **Schema**, troverai dei resolver, alcuni dei seguenti resolver.
+ Crea `Mutation.createTodo` un tramite il campo. `todo`
+ Aggiorna un `todo` messaggio tramite il `Mutation.updateTodo` campo.
+ Elimina un `todo` tramite il `Mutation.deleteTodo` campo.
+ Acquistane uno `todo` tramite il `Query.getTodo` campo.
+ Elenca tutto `todos` tramite il `Query.listTodos` campo.

Troverai campi e resolver simili allegati per il tipo. `Task` Diamo un'occhiata più da vicino ad alcuni resolver. 

### Mutazione. CreateToDo
<a name="createtodo"></a>

Dall'editor dello schema nella AWS AppSync console, sul lato destro, scegli accanto a. `testdb` `createTodo(...): Todo` Il codice del resolver utilizza la `insert` funzione del `rds` modulo per creare dinamicamente un'istruzione insert che aggiunge dati alla tabella. `todos` Poiché stiamo lavorando con Postgres, possiamo sfruttare l'`returning`istruzione per recuperare i dati inseriti.

Aggiorna il seguente resolver per specificare correttamente il tipo di campo. `DATE` `due`

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

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

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

Salva il resolver. Il suggerimento sul tipo contrassegna `due` correttamente il nostro oggetto di input come tipo. `DATE` Ciò consente al motore Postgres di interpretare correttamente il valore. Quindi, aggiorna lo schema per rimuovere il `id` `CreateTodo` dall'input. Poiché il nostro database Postgres può restituire l'ID generato, puoi fare affidamento su di esso per la creazione e la restituzione del risultato come una singola richiesta come segue.

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

Apporta la modifica e aggiorna lo schema. Vai all'editor **Queries** per aggiungere un elemento al database come segue.

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

Ottieni il seguente risultato.

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

### query.listToDos
<a name="listtodo"></a>

Dall'editor dello schema nella console, sul lato destro, scegli accanto a. `testdb` `listTodos(id: ID!): Todo` Il gestore delle richieste utilizza la funzione di utilità select per creare una richiesta in modo dinamico in fase di esecuzione.

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

Vogliamo filtrare in `todos` base alla `due` data. Aggiorniamo il resolver per trasmettere `due` i valori a. `DATE` Aggiorna l'elenco delle importazioni e il gestore delle richieste come segue.

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

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

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

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

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

Nell'editor **Queries**, procedi come segue.

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

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

Puoi anche un. `update` `Todo` Dall'editor di **Queries**, aggiorniamo il nostro primo `Todo` elemento di `id``1`.

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

Nota che devi specificare `id` l'elemento che stai aggiornando. Puoi anche specificare una condizione per aggiornare solo un elemento che soddisfa condizioni specifiche. Ad esempio, potremmo voler modificare l'elemento solo se la descrizione inizia con la `edits` seguente.

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

Proprio come abbiamo gestito le nostre `list` operazioni `create` e, allo stesso modo, possiamo aggiornare il nostro resolver per indirizzare il `due` campo a. `DATE` Salva queste modifiche come segue. `updateTodo`

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

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

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

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

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

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

Ora prova un aggiornamento con una condizione:

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

### Mutazione. deleteToDo
<a name="deletetodo"></a>

Puoi farlo `delete` con la mutazione. `Todo` `deleteTodo` Funziona come la `updateTodo` mutazione ed è necessario specificare `id` l'elemento che si desidera eliminare come segue.

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

### Scrittura di interrogazioni personalizzate
<a name="writing-custom-queries"></a>

Abbiamo usato le utilità del `rds` modulo per creare le nostre istruzioni SQL. Possiamo anche scrivere la nostra dichiarazione statica personalizzata per interagire con il nostro database. Innanzitutto, aggiorna lo schema per rimuovere il `id` campo dall'`CreateTask`input.

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

Quindi, crea un paio di attività. Un'attività ha una relazione a chiave esterna con `Todo` la seguente.

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

Crea un nuovo campo nel tuo `Query` tipo chiamato `getTodoAndTasks` come segue.

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

Aggiungi un `tasks` campo al `Todo` tipo come segue.

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

Salvare lo schema. Dall'editor di schemi nella console, sul lato destro, scegli **Attach Resolver for**. `getTodosAndTasks(id: Int!): Todo` Scegli la tua fonte di dati Amazon RDS. Aggiorna il tuo resolver con il seguente codice.

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

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

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

In questo codice, utilizziamo il modello di `sql` tag per scrivere un'istruzione SQL a cui possiamo passare in sicurezza un valore dinamico in fase di esecuzione. `createPgStatement`può richiedere fino a due richieste SQL alla volta. Lo usiamo per inviare una richiesta per la nostra `todo` e un'altra per la nostra`tasks`. Avresti potuto farlo con una `JOIN` dichiarazione o con qualsiasi altro metodo. L'idea è quella di poter scrivere la propria istruzione SQL per implementare la logica aziendale. Per utilizzare la query nell'editor **Queries**, procedi come segue.

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

## Eliminazione del cluster
<a name="rds-delete-cluster"></a>

**Importante**  
L'eliminazione di un cluster è permanente. Esamina attentamente il tuo progetto prima di eseguire questa azione.

Per eliminare il cluster:

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