Utilisation d'Aurora PostgreSQL avec l'API de données dans AWS AppSync - AWS AppSync GraphQL

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

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.

Configuration de votre base de données Aurora PostgreSQL

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.

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

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

Créez d'abord une base de données nomméeTESTDB. 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

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.

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

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

  4. Choisissez votre secret, puis choisissez Importer.

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

  6. 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 }
  7. 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 :

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:ExecuteStatement", ], "Resource": [ "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial", "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:your:secret:arn:appsync-tutorial-rds-secret", "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:your:secret:arn: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

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.

  • todosRé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

Dans l'éditeur de schéma de la AWS AppSync console, sur le côté droit, choisissez à testdb côté decreateTodo(...): 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'CreateTodoentré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

Dans l'éditeur de schéma de la console, sur le côté droit, choisissez à testdb côté delistTodos(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

Vous pouvez également update unTodo. Dans l'éditeur de requêtes, mettons à jour notre premier Todo élément de id1.

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

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

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'CreateTaskentré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 forgetTodosAndTasks(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. createPgStatementpeut traiter jusqu'à deux requêtes SQL à la fois. Nous l'utilisons pour envoyer une requête pour notre todo et une autre pour notretasks. 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

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