Referência de função do resolvedor de JavaScript do AWS AppSync para Amazon RDS
A função e o resolvedor do AWS AppSync RDS permitem que os desenvolvedores enviem consultas SQL a um banco de dados de cluster do Amazon Aurora usando a API de dados do RDS e recuperem o resultado dessas consultas. É possível redigir declarações SQL que são enviadas à API de dados usando o modelo marcado com sql do módulo do rds do AWS AppSync ou usando as funções auxiliadores select, insert, update e remove do módulo do rds. O AWS AppSync utiliza a ação ExecuteStatement do RDS Data Service para executar declarações SQL no banco de dados.
Tópicos
Modelo marcado com SQL
O modelo marcado com sql do AWS AppSync permite criar uma declaração estática que possa receber valores dinâmicos em runtime usando expressões de modelo. O AWS AppSync cria um mapa variável pelos valores da expressão para criar uma consulta SqlParameterized que é enviada à a API de dados sem servidor do Amazon Aurora. Com esse método, não é possível que valores dinâmicos transmitidos em runtime modifiquem a declaração original, o que pode causar uma execução não intencional. Todos os valores dinâmicos são transmitidos como parâmetros, não podem modificar a declaração original e não são executados pelo banco de dados. Isso torna a consulta menos vulnerável a ataques de injeção de SQL.
nota
Em todos os casos, ao redigir declarações SQL, é necessário seguir as diretrizes de segurança para lidar adequadamente com os dados recebidos como entrada.
nota
O modelo marcado com sql só aceita a transmissão de valores de variáveis. Não é possível usar uma expressão para especificar dinamicamente nomes de colunas ou de tabelas. No entanto, é possível usar funções de utilitário para criar declarações dinâmicas.
No exemplo a seguir, criamos uma consulta que filtra com base no valor do argumento col definido dinamicamente na consulta do GraphQL em runtime. O valor só pode ser adicionado à declaração usando a expressão de tag:
import { sql, createMySQLStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const query = sql` SELECT * FROM table WHERE column = ${ctx.args.col}` ; return createMySQLStatement(query); }
Ao transmitir todos os valores dinâmicos pelo mapa de variáveis, contamos com o mecanismo de banco de dados para processar e higienizar os valores com segurança.
Criar declarações
Funções e resolvedores podem interagir com bancos de dados MySQL e PostgreSQL. Use createMySQLStatement e createPgStatement, respectivamente, para criar declarações. Por exemplo, createMySQLStatement pode criar uma consulta MySQL. Essas funções aceitam até duas declarações, o que é útil quando uma solicitação deve recuperar os resultados imediatamente. Com MySQL, é possível fazer o seguinte:
import { sql, createMySQLStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { id, text } = ctx.args; const s1 = sql`insert into Post(id, text) values(${id}, ${text})`; const s2 = sql`select * from Post where id = ${id}`; return createMySQLStatement(s1, s2); }
nota
createPgStatement e createMySQLStatement não inserem caracteres de escape nem citam declarações criadas com o modelo marcado com sql.
Recuperação de dados
O resultado da declaração SQL executada está disponível no manipulador de respostas, no objeto context.result. O resultado é uma string JSON com os elementos de resposta da ação ExecuteStatement. Quando analisado, o resultado tem o seguinte formato:
type SQLStatementResults = { sqlStatementResults: { records: any[]; columnMetadata: any[]; numberOfRecordsUpdated: number; generatedFields?: any[] }[] }
É possível usar o utilitário toJsonObject para transformar o resultado em uma lista de objetos JSON representando as linhas exibidas. Por exemplo:
import { toJsonObject } from '@aws-appsync/utils/rds'; export function response(ctx) { const { error, result } = ctx; if (error) { return util.appendError( error.message, error.type, result ) } return toJsonObject(result)[1][0] }
Observe que o toJsonObject exibe uma matriz de resultados das declarações. Se você forneceu uma declaração, o tamanho da matriz será 1. Se você forneceu duas declarações, o tamanho da matriz será 2. Cada resultado na matriz contém 0 ou mais linhas. toJsonObject exibirá null se o valor do resultado for inválido ou inesperado.
Funções do utilitário
É possível usar os auxiliares utilitários do módulo do AWS AppSync RDS para interagir com o banco de dados.
O utilitário select cria uma declaração SELECT para consultar o banco de dados relacional.
Uso básico
Na forma básica, é possível especificar a tabela que deseja consultar:
import { select, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { // Generates statement: // "SELECT * FROM "persons" return createPgStatement(select({table: 'persons'})); }
Observe também que é possível especificar o esquema no identificador da tabela:
import { select, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { // Generates statement: // SELECT * FROM "private"."persons" return createPgStatement(select({table: 'private.persons'})); }
Especificar colunas
É possível especificar colunas com a propriedade columns. Se isso não for definido como um valor, o padrão será *:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" return createPgStatement(select({ table: 'persons', columns: ['id', 'name'] })); }
Também é possível especificar a tabela de uma coluna:
export function request(ctx) { // Generates statement: // SELECT "id", "persons"."name" // FROM "persons" return createPgStatement(select({ table: 'persons', columns: ['id', 'persons.name'] })); }
Limites e deslocamentos
É possível aplicar limit e offset à consulta:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" // LIMIT :limit // OFFSET :offset return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], limit: 10, offset: 40 })); }
Ordenar por
É possível classificar os resultados com a propriedade orderBy. Forneça uma matriz de objetos especificando a coluna e uma propriedade dir opcional:
export function request(ctx) { // Generates statement: // SELECT "id", "name" FROM "persons" // ORDER BY "name", "id" DESC return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], orderBy: [{column: 'name'}, {column: 'id', dir: 'DESC'}] })); }
Filtros
É possível criar filtros usando o objeto de condição especial:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" // WHERE "name" = :NAME return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], where: {name: {eq: 'Stephane'}} })); }
Também é possível combinar filtros:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" // WHERE "name" = :NAME and "id" > :ID return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], where: {name: {eq: 'Stephane'}, id: {gt: 10}} })); }
Também é possível criar declarações OR:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" // WHERE "name" = :NAME OR "id" > :ID return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], where: { or: [ { name: { eq: 'Stephane'} }, { id: { gt: 10 } } ]} })); }
Também é possível negar uma condição com not:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" // WHERE NOT ("name" = :NAME AND "id" > :ID) return createPgStatement(select({ table: 'persons', columns: ['id', 'name'], where: { not: [ { name: { eq: 'Stephane'} }, { id: { gt: 10 } } ]} })); }
Também é possível usar os seguintes operadores para comparar valores:
| Operador | Descrição | Tipos de valores possíveis |
|---|---|---|
| eq | Equal | number, string, boolean |
| ne | Not equal | number, string, boolean |
| le | Less than or equal | number, string |
| lt | Less than | number, string |
| ge | Greater than or equal | number, string |
| gt | Greater than | number, string |
| contains | Like | string |
| notContains | Not like | string |
| beginsWith | Starts with prefix | string |
| between | Between two values | number, string |
| attributeExists | The attribute is not null | number, string, boolean |
| size | checks the length of the element | string |
O utilitário insert oferece uma maneira simples de inserir itens de linha única no banco de dados com a operação INSERT.
Inserções de item único
Para inserir um item, especifique a tabela e, depois, transmita o objeto de valores. As chaves do objeto são associadas às colunas da tabela. São inseridos automaticamente caracteres de escape nos nomes das colunas e os valores são enviados ao banco de dados usando o mapa de variáveis:
import { insert, createMySQLStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: values } = ctx.args; const insertStatement = insert({ table: 'persons', values }); // Generates statement: // INSERT INTO `persons`(`name`) // VALUES(:NAME) return createMySQLStatement(insertStatement) }
Caso de uso do MySQL
É possível combinar um insert seguido por um select para recuperar a linha inserida:
import { insert, select, createMySQLStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: values } = ctx.args; const insertStatement = insert({ table: 'persons', values }); const selectStatement = select({ table: 'persons', columns: '*', where: { id: { eq: values.id } }, limit: 1, }); // Generates statement: // INSERT INTO `persons`(`name`) // VALUES(:NAME) // and // SELECT * // FROM `persons` // WHERE `id` = :ID return createMySQLStatement(insertStatement, selectStatement) }
Caso de uso do Postgres
Com o Postgres, é possível usar returning* ou uma matriz de nomes de colunas:
import { insert, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: values } = ctx.args; const insertStatement = insert({ table: 'persons', values, returning: '*' }); // Generates statement: // INSERT INTO "persons"("name") // VALUES(:NAME) // RETURNING * return createPgStatement(insertStatement) }
O utilitário update permite atualizar as linhas existentes. É possível usar o objeto de condição para aplicar alterações às colunas especificadas em todas as linhas que atendam à condição. Por exemplo, digamos que temos um esquema que nos permita fazer essa mutação. Queremos atualizar o name de Person com o valor id de 3, mas somente se os conhecermos (known_since) desde o ano 2000:
mutation Update { updatePerson( input: {id: 3, name: "Jon"}, condition: {known_since: {ge: "2000"}} ) { id name } }
Nosso resolvedor de atualização é semelhante a:
import { update, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: { id, ...values }, condition } = ctx.args; const where = { ...condition, id: { eq: id }, }; const updateStatement = update({ table: 'persons', values, where, returning: ['id', 'name'], }); // Generates statement: // UPDATE "persons" // SET "name" = :NAME, "birthday" = :BDAY, "country" = :COUNTRY // WHERE "id" = :ID // RETURNING "id", "name" return createPgStatement(updateStatement) }
Podemos adicionar uma verificação à nossa condição para garantir que somente a linha que tem a chave primária id igual a 3 seja atualizada. Da mesma forma, para Postgres inserts, é possível usar returning para exibir os dados modificados.
O utilitário remove permite excluir as linhas existentes. É possível usar o objeto de condição em todas as linhas que atendam à condição. Observe que delete é uma palavra-chave reservada em JavaScript. remove deve ser usado:
import { remove, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: { id }, condition } = ctx.args; const where = { ...condition, id: { eq: id } }; const deleteStatement = remove({ table: 'persons', where, returning: ['id', 'name'], }); // Generates statement: // DELETE "persons" // WHERE "id" = :ID // RETURNING "id", "name" return createPgStatement(updateStatement) }
Conversão
Em alguns casos, convém ter maior especificidade sobre o tipo de objeto correto a ser usado na declaração. É possível usar as dicas de tipo fornecidas para especificar o tipo dos parâmetros. AWS AppSync é compatível com os mesmos tipos de dica da API de dados. É possível converter parâmetros usando as funções typeHint do módulo do AWS AppSync rds.
O exemplo a seguir permite enviar uma matriz como um valor que é convertido como um objeto JSON. Usamos o operador -> para recuperar o elemento index 2 na matriz JSON:
import { sql, createPgStatement, toJsonObject, typeHint } from '@aws-appsync/utils/rds'; export function request(ctx) { const arr = ctx.args.list_of_ids const statement = sql`select ${typeHint.JSON(arr)}->2 as value` return createPgStatement(statement) } export function response(ctx) { return toJsonObject(ctx.result)[0][0].value }
A conversão também é útil ao processar e comparar DATE, TIME e TIMESTAMP:
import { select, createPgStatement, typeHint } from '@aws-appsync/utils/rds'; export function request(ctx) { const when = ctx.args.when const statement = select({ table: 'persons', where: { createdAt : { gt: typeHint.DATETIME(when) } } }) return createPgStatement(statement) }
Veja outro exemplo de como enviar a data e a hora atuais:
import { sql, createPgStatement, typeHint } from '@aws-appsync/utils/rds'; export function request(ctx) { const now = util.time.nowFormatted('YYYY-MM-dd HH:mm:ss') return createPgStatement(sql`select ${typeHint.TIMESTAMP(now)}`) }
Dicas de tipo disponíveis
-
typeHint.DATE: o parâmetro correspondente é enviado como objeto do tipoDATEao banco de dados. O formato aceito éYYYY-MM-DD. -
typeHint.DECIMAL: o parâmetro correspondente é enviado como objeto do tipoDECIMALao banco de dados. -
typeHint.JSON: o parâmetro correspondente é enviado como objeto do tipoJSONao banco de dados. -
typeHint.TIME: o valor de parâmetro de string correspondente é enviado como objeto do tipoTIMEao banco de dados. O formato aceito éHH:MM:SS[.FFF]. -
typeHint.TIMESTAMP: o valor de parâmetro de string correspondente é enviado como objeto do tipoTIMESTAMPao banco de dados. O formato aceito éYYYY-MM-DD HH:MM:SS[.FFF]. -
typeHint.UUID: o valor de parâmetro de string correspondente é enviado como objeto do tipoUUIDao banco de dados.