

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Utilisation d'Aurora PostgreSQL avec l'API de données dans AWS AppSync
<a name="aurora-serverless-tutorial-js"></a>

 

Découvrez comment connecter votre API GraphQL aux bases de données Aurora PostgreSQL à l'aide de. AWS AppSync Cette intégration vous permet de créer des applications évolutives pilotées par les données en exécutant des requêtes SQL et des mutations via des opérations GraphQL. AWS AppSync fournit une source de données pour exécuter des instructions SQL sur des clusters Amazon Aurora activés par une API de données. Vous pouvez utiliser des AWS AppSync résolveurs pour exécuter des instructions SQL sur l'API de données à l'aide de requêtes GraphQL, de mutations et d'abonnements.

Avant de commencer ce didacticiel, vous devez avoir une connaissance de base des AWS services et des concepts GraphQL.

**Note**  
Ce didacticiel utilise la région `US-EAST-1`. 

**Topics**
+ [Configuration de votre base de données Aurora PostgreSQL](#creating-clusters)
+ [Création de la base de données et de la table](#creating-db-table)
+ [Création d'un schéma GraphQL](#rds-graphql-schema)
+ [Résolveurs pour RDS](#rds-resolvers)
+ [Supprimer votre cluster](#rds-delete-cluster)

## Configuration de votre base de données Aurora PostgreSQL
<a name="creating-clusters"></a>

Avant d'ajouter une source de données Amazon RDS à AWS AppSync, procédez comme suit.

1. Activez une API de données sur un cluster Aurora Serverless v2.

1. Configurez un secret à l'aide de AWS Secrets Manager

1. Créez le cluster à l'aide de la AWS CLI commande suivante.

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

Cela renverra un ARN pour le cluster. Après avoir créé un cluster, vous devez ajouter une instance Serverless v2 à l'aide de la AWS CLI commande suivante.

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

**Note**  
Ces points de terminaison mettent du temps à s'activer. Vous pouvez vérifier leur statut dans la console RDS dans l'onglet **Connectivité et sécurité** du cluster.

Vérifiez l'état du cluster à l'aide de la AWS CLI commande suivante.

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

Créez un secret via la AWS Secrets Manager console ou AWS CLI avec un fichier d'entrée tel que le suivant en utilisant le `USERNAME` et `COMPLEX_PASSWORD` de l'étape précédente :

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

Passez ceci en tant que paramètre à AWS CLI :

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

Cela renverra un ARN pour le secret. **Prenez note** de l'ARN de votre cluster Aurora Serverless v2 et de Secret pour plus tard lors de la création d'une source de données dans la AWS AppSync console. 

## Création de la base de données et de la table
<a name="creating-db-table"></a>

Créez d'abord une base de données nommée`TESTDB`. Dans PostgreSQL, une base de données est un conteneur qui contient des tables et d'autres objets SQL. Vérifiez que votre cluster Aurora Serverless v2 est correctement configuré avant de l'ajouter à votre AWS AppSync API. Créez d'abord une base de données *TESTDB* avec le `--sql` paramètre suivant.

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

 Si cela fonctionne sans erreur, ajoutez deux tables avec la `create table` commande :

```
 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);'
```

En cas de succès, ajoutez le cluster en tant que source de données dans votre API.

## Création d'un schéma GraphQL
<a name="rds-graphql-schema"></a>

Maintenant que votre API de données Aurora Serverless v2 s'exécute avec des tables configurées, nous allons créer un schéma GraphQL. Vous pouvez créer rapidement votre API en important des configurations de tables depuis une base de données existante à l'aide de l'assistant de création d'API.

Pour commencer : 

1. Dans la AWS AppSync console, choisissez **Create API**, puis **Start with a Amazon Aurora cluster**. 

1. Spécifiez les détails de **l'API, tels que le nom** de l'API, puis sélectionnez votre base de données pour générer l'API.

1. Choisissez votre base de données. Si nécessaire, mettez à jour la région, puis choisissez votre cluster Aurora et votre base de données *TESTDB*. 

1. Choisissez votre secret, puis choisissez **Importer**. 

1. Une fois les tables découvertes, mettez à jour les noms des types. Changez `Todos` vers `Todo` et `Tasks` vers`Task`. 

1. Prévisualisez le schéma généré en choisissant **Aperçu du schéma**. Votre schéma ressemblera à ceci : 

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

1. Pour le rôle, vous pouvez soit AWS AppSync créer un nouveau rôle, soit en créer un avec une politique similaire à celle ci-dessous :

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

------

   Notez que cette politique contient deux déclarations auxquelles vous accordez un accès aux rôles. La première ressource est votre cluster Aurora et la seconde est votre AWS Secrets Manager ARN. 

   Choisissez **Next**, passez en revue les détails de configuration, puis choisissez **Create API**. Vous disposez désormais d'une API entièrement opérationnelle. Vous pouvez consulter tous les détails de votre API sur la page **Schéma**. 

## Résolveurs pour RDS
<a name="rds-resolvers"></a>

Le flux de création d'API a automatiquement créé les résolveurs pour interagir avec nos types. Si vous regardez la page **Schéma**, vous trouverez des résolveurs parmi les résolveurs suivants.
+ Créez un `todo` via le `Mutation.createTodo` champ.
+ Mettez à jour un `todo` via le `Mutation.updateTodo` champ.
+ Supprimez un `todo` via le `Mutation.deleteTodo` champ.
+ Obtenez-en un `todo` sur le `Query.getTodo` terrain.
+ `todos`Répertoriez tout via le `Query.listTodos` champ.

Vous trouverez des champs et des résolveurs similaires attachés au `Task` type. Examinons de plus près certains des résolveurs. 

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

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez à `testdb` côté de`createTodo(...): Todo`. Le code du résolveur utilise la `insert` fonction du `rds` module pour créer dynamiquement une instruction d'insertion qui ajoute des données à la `todos` table. Comme nous travaillons avec Postgres, nous pouvons tirer parti de `returning` cette instruction pour récupérer les données insérées.

Mettez à jour le résolveur suivant pour spécifier correctement le `DATE` type du `due` champ.

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

Enregistrez le résolveur. L'indice de type marque le type `due` correct dans notre objet d'entrée en tant que `DATE` type. Cela permet au moteur Postgres d'interpréter correctement la valeur. Ensuite, mettez à jour votre schéma pour le supprimer `id` de l'`CreateTodo`entrée. Notre base de données Postgres pouvant renvoyer l'identifiant généré, vous pouvez vous y fier pour la création et le renvoi du résultat sous forme de requête unique, comme suit.

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

Apportez la modification et mettez à jour votre schéma. Accédez à l'éditeur de **requêtes** pour ajouter un élément à la base de données comme suit.

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

Vous obtenez le résultat suivant.

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

Dans l'éditeur de schéma de la console, sur le côté droit, choisissez à `testdb` côté de`listTodos(id: ID!): Todo`. Le gestionnaire de demandes utilise la fonction utilitaire de sélection pour créer une demande de manière dynamique au moment de l'exécution.

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

Nous voulons filtrer `todos` en fonction de la `due` date. Mettons à jour le résolveur vers lequel convertir `due` `DATE` les valeurs. Mettez à jour la liste des importations et le gestionnaire de demandes comme suit.

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

Dans l'éditeur de **requêtes**, procédez comme suit.

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

### Mutation. Mise à jour à faire
<a name="updatetodo"></a>

Vous pouvez également `update` un`Todo`. Dans l'éditeur de **requêtes**, mettons à jour notre premier `Todo` élément de `id``1`.

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

Notez que vous devez spécifier `id` l'élément que vous mettez à jour. Vous pouvez également spécifier une condition pour ne mettre à jour qu'un élément répondant à des conditions spécifiques. Par exemple, il se peut que nous souhaitions modifier l'article uniquement si la description commence `edits` comme suit.

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

Tout comme nous avons géré nos `create` `list` opérations, nous pouvons mettre à jour notre résolveur pour transformer le `due` champ en un`DATE`. Enregistrez ces modifications `updateTodo` comme suit.

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

Maintenant, essayez une mise à jour avec une condition :

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

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

Vous pouvez `delete` `Todo` utiliser la `deleteTodo` mutation. Cela fonctionne comme la `updateTodo` mutation, et vous devez spécifier `id` l'élément que vous souhaitez supprimer comme suit.

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

### Rédaction de requêtes personnalisées
<a name="writing-custom-queries"></a>

Nous avons utilisé les utilitaires du `rds` module pour créer nos instructions SQL. Nous pouvons également écrire notre propre déclaration statique personnalisée pour interagir avec notre base de données. Commencez par mettre à jour le schéma pour supprimer le `id` champ de l'`CreateTask`entrée.

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

Créez ensuite quelques tâches. Une tâche possède une relation de clé étrangère `Todo` comme suit.

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

Créez un nouveau champ de votre `Query` type appelé `getTodoAndTasks` comme suit.

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

Ajoutez un `tasks` champ au `Todo` type comme suit.

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

Enregistrez le schéma. Dans l'éditeur de schéma de la console, sur le côté droit, choisissez **Attach Resolver** for`getTodosAndTasks(id: Int!): Todo`. Choisissez votre source de données Amazon RDS. Mettez à jour votre résolveur avec le code suivant.

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

Dans ce code, nous utilisons le modèle de `sql` balise pour écrire une instruction SQL à laquelle nous pouvons transmettre une valeur dynamique en toute sécurité lors de l'exécution. `createPgStatement`peut traiter jusqu'à deux requêtes SQL à la fois. Nous l'utilisons pour envoyer une requête pour notre `todo` et une autre pour notre`tasks`. Vous auriez pu le faire avec une `JOIN` déclaration ou toute autre méthode d'ailleurs. L'idée est de pouvoir écrire votre propre instruction SQL pour implémenter votre logique métier. Pour utiliser la requête dans l'éditeur de **requêtes**, procédez comme suit.

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

## Supprimer votre cluster
<a name="rds-delete-cluster"></a>

**Important**  
La suppression d'un cluster est définitive. Passez en revue votre projet de manière approfondie avant de réaliser cette action.

Pour supprimer votre cluster :

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