Utilisation d'Aurora Postgre SQL avec des données API dans AWS AppSync - AWS AppSync

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 Postgre SQL avec des données API dans AWS AppSync

AWS AppSync fournit une source de données pour exécuter SQL des instructions sur des clusters Amazon Aurora activés avec une donnéeAPI. Vous pouvez utiliser des AWS AppSync résolveurs pour exécuter SQL des instructions sur les données à l'APIaide de requêtes GraphQL, de mutations et d'abonnements.

Note

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

Création de clusters

Avant d'ajouter une source de RDS données Amazon AWS AppSync, activez d'abord une donnée API sur un cluster Aurora Serverless. Vous devez également configurer un secret à l'aide de AWS Secrets Manager. Pour créer un cluster Aurora Serverless, vous pouvez utiliser : AWS CLI

aws rds create-db-cluster \ --db-cluster-identifier appsync-tutorial \ --engine aurora-postgresql --engine-version 13.11 \ --engine-mode serverless \ --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD

Cela renverra un ARN pour le cluster. Vous pouvez vérifier l'état de votre cluster à l'aide de la 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 à CLI :

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

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

Données habilitantes API

Une fois que le statut de votre cluster est passé àavailable, activez les données API en suivant la RDSdocumentation Amazon. Les données API doivent être activées avant de les ajouter en tant que source de AWS AppSync données. Vous pouvez également activer les données API à l'aide de AWS CLI :

aws rds modify-db-cluster \ --db-cluster-identifier appsync-tutorial \ --enable-http-endpoint \ --apply-immediately

Création de la base de données et de la table

Après avoir activé vos donnéesAPI, validez qu'elles fonctionnent à l'aide de la aws rds-data execute-statement commande du AWS CLI. Cela garantit que votre cluster Aurora Serverless est correctement configuré avant de l'ajouter au AWS AppSync API. Créez d'abord une TESTDBbase de données avec le --sql paramètre :

aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:123456789012:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:123456789012:secret:appsync-tutorial-rds-secret" \ --sql "create DATABASE \"testdb\""

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:123456789012:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:123456789012: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:123456789012:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:123456789012: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);'

Si tout fonctionne sans problème, vous pouvez désormais ajouter le cluster en tant que source de données dans votreAPI.

Création d'un schéma GraphQL

Maintenant que vos données Aurora Serverless API s'exécutent avec des tables configurées, nous allons créer un schéma GraphQL. Vous pouvez le faire manuellement, mais cela vous AWS AppSync permet de démarrer rapidement en important la configuration des tables depuis une base de données existante à l'aide de l'assistant de API création.

Pour commencer :

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

  2. Spécifiez des API détails tels que le APInom, puis sélectionnez votre base de données pour générer leAPI.

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

  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:123456789012:cluster:appsync-tutorial", "arn:aws:rds:us-east-1:123456789012:cluster:appsync-tutorial:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:your:secret:arn:appsync-tutorial-rds-secret", "arn:aws:secretsmanager:us-east-1:123456789012: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 Suivant, passez en revue les détails de configuration, puis choisissez Créer API. Vous disposez désormais d'un système totalement opérationnelAPI. Vous pouvez consulter tous les détails de votre schéma API sur la page Schéma.

Résolveurs pour RDS

Le flux de API création a automatiquement créé les résolveurs pour interagir avec nos types. Si vous regardez la page Schéma, vous trouverez les résolveurs nécessaires pour :

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

Mettons à jour le résolveur 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. Comme notre base de données Postgres peut renvoyer l'identifiant généré, nous pouvons nous y fier pour la création et le renvoi du résultat sous la forme d'une seule requête :

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 :

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

Vous obtenez le résultat :

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

Requête. 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 :

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

Essayons la requête. Dans l'éditeur de requêtes :

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

Mutation. updateTodo

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 par edits :

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

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 :

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 SQL instructions. 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 avec Todo :

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 :

getTodoAndTasks(id: Int!): Todo

Ajoutez un tasks champ au Todo type :

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 RDS de données Amazon. 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 SQL instruction à laquelle nous pouvons transmettre une valeur dynamique en toute sécurité lors de l'exécution. createPgStatementpeut traiter jusqu'à deux SQL demandes à 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 rédiger votre propre SQL déclaration pour mettre en œuvre votre logique métier. Pour utiliser la requête dans l'éditeur de requêtes, nous pouvons essayer ceci :

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