As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Uso do Aurora PostgreSQL com a API de dados no AWS AppSync
Saiba como conectar sua API GraphQL aos bancos de dados Aurora PostgreSQL usando o. AWS AppSync Essa integração permite que você crie aplicativos escaláveis e orientados por dados executando consultas e mutações SQL por meio de operações do GraphQL. AWS AppSync fornece uma fonte de dados para executar instruções SQL em clusters do Amazon Aurora que são habilitados com uma API de dados. Você pode usar AWS AppSync resolvedores para executar instruções SQL na API de dados com consultas, mutações e assinaturas do GraphQL.
Antes de começar este tutorial, você deve ter familiaridade básica com AWS serviços e conceitos do GraphQL.
nota
Este tutorial usa a Região US-EAST-1
.
Tópicos
Configurar seu banco de dados Aurora PostgreSQL
Antes de adicionar uma fonte de dados do Amazon RDS AWS AppSync, faça o seguinte.
Ative uma API de dados em um cluster Aurora Serverless v2.
Configure um segredo usando AWS Secrets Manager
Crie o cluster usando o AWS CLI comando a seguir.
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
Isso retornará um ARN para o cluster. Depois de criar um cluster, você deve adicionar uma instância Serverless v2 com o comando a seguir. 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
Esses endpoints demoram para serem ativados. Você pode verificar seu status no console do RDS na guia Conectividade e segurança do cluster.
Verifique o status do cluster com o AWS CLI comando a seguir.
aws rds describe-db-clusters \ --db-cluster-identifier appsync-tutorial \ --query "DBClusters[0].Status"
Crie um segredo por meio do AWS Secrets Manager console ou AWS CLI com um arquivo de entrada, como o seguinte, usando o USERNAME
e COMPLEX_PASSWORD
da etapa anterior:
{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }
Passe isso como um parâmetro para AWS CLI:
aws secretsmanager create-secret \ --name appsync-tutorial-rds-secret \ --secret-string file://creds.json
Isso retornará um ARN para o segredo. Anote o ARN do seu cluster Aurora Serverless v2 e do Secret para mais tarde ao criar uma fonte de dados no console. AWS AppSync
Criar o banco de dados e uma tabela
Primeiro, crie um banco de dados chamadoTESTDB
. No PostgreSQL, um banco de dados é um contêiner que contém tabelas e outros objetos SQL. Valide se seu cluster Aurora Serverless v2 está configurado corretamente antes de adicioná-lo à sua API. AWS AppSync Primeiro, crie um banco de dados TESTDB com o --sql
parâmetro a seguir.
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 isso for executado sem erros, inclua duas tabelas com o comando create table
:
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);'
Se for bem-sucedido, adicione o cluster como fonte de dados na sua API.
Criar um esquema do GraphQL
Agora que sua API de dados Aurora Serverless v2 está sendo executada com tabelas configuradas, criaremos um esquema GraphQL. Você pode criar rapidamente sua API importando configurações de tabela de um banco de dados existente usando o assistente de criação de API.
Para começar:
-
No AWS AppSync console, escolha Create API e, em seguida, Start with an Amazon Aurora cluster.
-
Especifique os detalhes da API, como Nome da API, e selecione o banco de dados para gerar a API.
-
Selecione o banco de dados. Se necessário, atualize a região e selecione o cluster do Aurora e o banco de dados TESTDB.
-
Selecione o segredo e escolha Importar.
-
Depois que as tabelas forem descobertas, atualize os nomes dos tipos. Altere
Todos
paraTodo
eTasks
paraTask
. -
Visualize o esquema gerado selecionando Visualizar esquema. O esquema terá a seguinte aparência:
type Todo { id: Int! description: String! due: AWSDate! createdAt: String } type Task { id: Int! todoId: Int! description: String }
-
Para a função, você pode AWS AppSync criar uma nova função ou criar uma com uma política semelhante à abaixo:
{ "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:*" ] } ] }
Observe que há duas declarações nesta política à qual você está concedendo acesso ao perfil. O primeiro recurso é o cluster do Aurora e o segundo é o ARN do AWS Secrets Manager .
Selecione Próximo, revise os detalhes da configuração e escolha Criar API. Agora você tem uma API totalmente operacional. É possível revisar os detalhes completos da API na página Esquema.
Resolvedores para RDS
O fluxo de criação da API criou automaticamente os resolvedores para interagir com nossos tipos. Se você consultar a página Esquema, encontrará alguns dos seguintes resolvedores.
-
Criar um
todo
por meio do campoMutation.createTodo
. -
Atualizar um
todo
por meio do campoMutation.updateTodo
. -
Excluir um
todo
por meio do campoMutation.deleteTodo
. -
Obter um único
todo
por meio do campoQuery.getTodo
. -
Listar todos os
todos
por meio do campoQuery.listTodos
.
Você encontrará campos e resolvedores semelhantes anexados para o tipo Task
. Vamos examinar com mais cuidado alguns dos resolvedores.
Mutation.createTodo
No editor de esquemas no AWS AppSync console, no lado direito, escolha testdb
próximo a. createTodo(...): Todo
O código do resolvedor usa a função insert
do módulo do rds
para criar dinamicamente uma declaração de inserção que adiciona dados à tabela todos
. Como estamos trabalhando com o Postgres, podemos aproveitar a declaração returning
para recuperar os dados inseridos.
Atualize o resolvedor a seguir para especificar corretamente o DATE
tipo do due
campo.
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] }
Salve o resolvedor. A dica de tipo marca a propriedade due
no objeto de entrada como um tipo DATE
. Isso permite que o mecanismo Postgres interprete adequadamente o valor. Depois, atualize o esquema para remover o id
da entrada CreateTodo
. Como nosso banco de dados Postgres pode retornar o ID gerado, você pode confiar nele para criar e retornar o resultado como uma única solicitação, da seguinte forma.
input CreateTodoInput { due: AWSDate! createdAt: String description: String! }
Faça a alteração e atualize o esquema. Acesse o editor de consultas para adicionar um item ao banco de dados da seguinte maneira.
mutation CreateTodo { createTodo(input: {description: "Hello World!", due: "2023-12-31"}) { id due description createdAt } }
Você obtém o seguinte resultado.
{ "data": { "createTodo": { "id": 1, "due": "2023-12-31", "description": "Hello World!", "createdAt": "2023-11-14 20:47:11.875428" } } }
Query.listTodos
No editor de esquemas no console, à direita, selecione testdb
ao lado de listTodos(id: ID!): Todo
. O manipulador de solicitações usa a função de utilitário select para criar uma solicitação dinamicamente em runtime.
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) }
Queremos filtrar todos
com base na data due
. Vamos atualizar o resolvedor no qual converter valores due
em DATE
. Atualize a lista de importações e o manipulador de solicitações da seguinte maneira.
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 }; }
No editor de consultas, faça o seguinte.
query LIST { listTodos(limit: 10, filter: {due: {between: ["2021-01-01", "2025-01-02"]}}) { items { id due description } } }
Mutation.updateTodo
Também é possível update
um Todo
. No editor de consultas, vamos atualizar o primeiro item Todo
de id
1
.
mutation UPDATE { updateTodo(input: {id: 1, description: "edits"}) { description due id } }
Observe que é preciso especificar o id
do item que você está atualizando. Também é possível determinar uma condição para atualizar somente um item que atenda a condições específicas. Por exemplo, podemos querer editar o item somente se a descrição começar da edits
seguinte forma.
mutation UPDATE { updateTodo(input: {id: 1, description: "edits: make a change"}, condition: {description: {beginsWith: "edits"}}) { description due id } }
Assim como processamos as operações create
e list
, podemos atualizar o resolvedor para converter o campo due
em DATE
. Salve essas alterações da updateTodo
seguinte forma.
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]; }
Agora tente uma atualização com uma condição:
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
É possível delete
um Todo
com a mutação deleteTodo
. Isso funciona como a updateTodo
mutação, e você deve especificar o item que deseja excluir id
da seguinte maneira.
mutation DELETE { deleteTodo(input: {id: 1}) { description due id } }
Redigir consultas personalizadas
Usamos os utilitários do módulo do rds
para criar declarações SQL. Também podemos redigir a própria declaração estática personalizada para interagir com o banco de dados. Primeiro, atualize o esquema para remover o id
da entrada CreateTask
.
input CreateTaskInput { todoId: Int! description: String }
Depois, crie algumas tarefas. Uma tarefa tem uma relação de chave estrangeira com a Todo
seguinte.
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 } }
Crie um novo campo em seu Query
tipo chamado da getTodoAndTasks
seguinte forma.
getTodoAndTasks(id: Int!): Todo
Adicione um tasks
campo ao Todo
tipo da seguinte maneira.
type Todo { due: AWSDate! id: Int! createdAt: String description: String! tasks:TaskConnection }
Salve o esquema. No editor de esquemas no console, à direita, selecione Anexar resolvedor para getTodosAndTasks(id:
Int!): Todo
. Selecione a fonte de dados do Amazon RDS. Atualize seu resolvedor com o código a seguir.
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; }
Nesse código, usamos o modelo de tag sql
para redigir uma declaração SQL para a qual podemos transmitir com segurança um valor dinâmico em runtime. createPgStatement
pode receber até duas solicitações SQL por vez. Usamos isso para enviar uma consulta ao todo
e outra para a tasks
. É possível fazer isso com uma declaração JOIN
ou qualquer outro método. A ideia é poder redigir a própria declaração SQL para implementar a lógica de negócios. Para usar a consulta no editor de consultas, faça o seguinte.
query TodoAndTasks { getTodosAndTasks(id: 2) { id due description tasks { items { id description } } } }
Excluir o cluster
Importante
A exclusão de um cluster é permanente. Revise o projeto com cuidado antes de realizar essa ação.
Para excluir o cluster:
$ aws rds delete-db-cluster \ --db-cluster-identifier appsync-tutorial \ --skip-final-snapshot