Referencia a la función de solucionador de JavaScript de AWS AppSync para Amazon RDS
La función y el solucionador de RDS AWS AppSync permiten a los desarrolladores enviar consultas SQL a una base de datos de clústeres de Amazon Aurora mediante la API de datos de RDS y obtener el resultado de estas consultas. Puede escribir instrucciones SQL que se envíen a la API de datos mediante la plantilla con etiquetas sql del módulo rds de AWS AppSync o mediante las funciones de ayuda select, insert, update y remove del módulo rds. AWS AppSync utiliza la acción ExecuteStatement del servicio de datos de RDS para ejecutar instrucciones SQL en la base de datos.
Temas
Plantilla con etiquetas SQL
La plantilla con etiquetas sql de AWS AppSync le permite crear una instrucción estática que puede recibir valores dinámicos en tiempo de ejecución mediante el uso de expresiones de plantilla. AWS AppSync crea un mapa de variables a partir de los valores de expresión para construir una consulta SqlParameterized que se envía a la API de datos de Amazon Aurora sin servidor. Con este método, no es posible que los valores dinámicos pasados en tiempo de ejecución modifiquen la instrucción original, lo que podría provocar una ejecución no intencionada. Todos los valores dinámicos se pasan como parámetros, no pueden modificar la instrucción original y la base de datos no los ejecuta. Esto hace que la consulta sea menos vulnerable a los ataques de inyección de SQL.
nota
En todos los casos, al escribir instrucciones SQL, debe seguir las pautas de seguridad para gestionar correctamente los datos que reciba como entrada.
nota
La plantilla con etiquetas sql solo admite la transferencia de valores variables. No puede usar una expresión para especificar dinámicamente los nombres de las columnas o tablas. No obstante, puede usar funciones de utilidad para crear instrucciones dinámicas.
En el siguiente ejemplo, creamos una consulta que filtra en función del valor del argumento col que se establece dinámicamente en la consulta GraphQL en tiempo de ejecución. El valor solo se puede añadir a la instrucción mediante la expresión de etiquetas:
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); }
Al pasar todos los valores dinámicos por el mapa de variables, confiamos en el motor de la base de datos para gestionar y sanear los valores de forma segura.
Creación de instrucciones
Las funciones y los solucionadores pueden interactuar con las bases de datos MySQL y PostgreSQL. Utilice createMySQLStatement y createPgStatement, respectivamente, para crear instrucciones. Por ejemplo, createMySQLStatement puede crear una consulta MySQL. Estas funciones aceptan hasta dos instrucciones, lo que resulta útil cuando una solicitud debe recuperar los resultados de forma inmediata. Con SQL, podría hacer:
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 y createMySQLStatement no escapa ni cita las instrucciones creadas con la plantilla etiquetada con sql.
Recuperación de datos
El resultado de la instrucción SQL ejecutada está disponible en el controlador de respuestas del objeto context.result. El resultado es una cadena JSON con los elementos de respuesta de la acción ExecuteStatement. Por ejemplo, puede utilizar la siguiente forma:
type SQLStatementResults = { sqlStatementResults: { records: any[]; columnMetadata: any[]; numberOfRecordsUpdated: number; generatedFields?: any[] }[] }
Puede utilizar la utilidad toJsonObject para transformar el resultado en una lista de objetos JSON que representen las filas devueltas. Por ejemplo:
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] }
Tenga en cuenta que toJsonObject devuelve una matriz de resultados de la instrucción. Si proporcionó una instrucción, la longitud de la matriz es 1. Si proporcionó dos instrucciones, la longitud de la matriz es 2. Cada resultado de la matriz contiene 0 o más filas. toJsonObject devuelve null si el valor del resultado no es válido o no es esperado.
Funciones de utilidad
Puede utilizar las aplicaciones auxiliares de utilidades del módulo AWS AppSync RDS para interactuar con su base de datos.
La utilidad select crea una instrucción SELECT para consultar la base de datos relacional.
Uso básico
En su forma básica, puede especificar la tabla que desea consultar:
import { select, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { // Generates statement: // "SELECT * FROM "persons" return createPgStatement(select({table: 'persons'})); }
Tenga en cuenta que también puede especificar el esquema en el identificador de la tabla:
import { select, createPgStatement } from '@aws-appsync/utils/rds'; export function request(ctx) { // Generates statement: // SELECT * FROM "private"."persons" return createPgStatement(select({table: 'private.persons'})); }
Especificación de columnas
Puede especificar columnas con la propiedad columns. Si no se establece en un valor, el valor predeterminado es *:
export function request(ctx) { // Generates statement: // SELECT "id", "name" // FROM "persons" return createPgStatement(select({ table: 'persons', columns: ['id', 'name'] })); }
También puede especificar la tabla de una columna:
export function request(ctx) { // Generates statement: // SELECT "id", "persons"."name" // FROM "persons" return createPgStatement(select({ table: 'persons', columns: ['id', 'persons.name'] })); }
Límites y desplazamientos
Puede aplicar limit y offset a la 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
Puede ordenar los resultados con la propiedad orderBy. Proporcione una matriz de objetos especificando la columna y una propiedad 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
Puede crear filtros con el objeto de condición 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'}} })); }
También puede 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}} })); }
También puede crear instrucciones 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 } } ]} })); }
También puede negar una condición con 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 } } ]} })); }
También puede utilizar los siguientes operadores para comparar valores:
| Operador | Descripción | Tipos de valor posibles |
|---|---|---|
| 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 |
La utilidad insert ofrece una forma sencilla de insertar elementos de una sola fila en la base de datos con la operación INSERT.
Inserciones de un solo elemento
Para insertar un elemento, especifique la tabla y, a continuación, transfiera su objeto de valores. Las claves de objetos se asignan a las columnas de la tabla. Los nombres de las columnas se escapan automáticamente y los valores se envían a la base de datos mediante el mapa de variables:
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 de MySQL
Puede combinar un insert seguido de un select para recuperar la fila insertada:
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 de Postgres
Con Postgres, puede usar returning* o una matriz de nombres de columna:
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) }
La utilidad update le permite actualizar las filas existentes. Puede utilizar el objeto de condición para aplicar cambios a las columnas especificadas en todas las filas que cumplan la condición. Por ejemplo, supongamos que tenemos un esquema que nos permite realizar esta mutación. Queremos actualizar el name de Person con el valor id de 3, pero solo si los conocemos (known_since) desde el año 2000:
mutation Update { updatePerson( input: {id: 3, name: "Jon"}, condition: {known_since: {ge: "2000"}} ) { id name } }
Nuestro solucionador de actualización tendrá este aspecto:
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 añadir una comprobación a nuestra condición para asegurarnos de que solo se actualice la fila en la que la clave principal 3 sea igual a id. Del mismo modo, en el caso de inserts de Postgres, se puede utilizar returning para devolver los datos modificados.
La utilidad remove le permite eliminar las filas existentes. Puede utilizar el objeto de condición en todas las filas que cumplan la condición. Tenga en cuenta que delete es una palabra clave reservada en JavaScript. En su lugar, debería usarse remove:
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) }
Casting
En algunos casos, es posible que desee más especificidad sobre el tipo de objeto correcto para usar en su instrucción. Puede utilizar las sugerencias de tipo proporcionadas para especificar el tipo de parámetros. AWS AppSync admite las mismas sugerencias de tipo que la API de datos. Puede convertir sus parámetros mediante las funciones typeHint del módulo rds de AWS AppSync.
En el siguiente ejemplo puede enviar una matriz como un valor que se convierte en un objeto JSON. Usamos el operador -> para recuperar el elemento en el index 2 en la 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 }
La conversión también resulta útil al manipular y comparar DATE, TIME y 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) }
Aquí se muestra otro ejemplo de cómo puede enviar la fecha y hora actuales:
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)}`) }
Sugerencias de tipos disponibles
-
typeHint.DATE: el parámetro correspondiente se envía como un objeto de tipoDATEa la base de datos. El formato aceptado esYYYY-MM-DD. -
typeHint.DECIMAL: el parámetro correspondiente se envía como un objeto de tipoDECIMALa la base de datos. -
typeHint.JSON: el parámetro correspondiente se envía como un objeto de tipoJSONa la base de datos. -
typeHint.TIME: el valor del parámetro de cadena correspondiente se envía como un objeto de tipoTIMEa la base de datos. El formato aceptado esHH:MM:SS[.FFF]. -
typeHint.TIMESTAMP: el valor del parámetro de cadena correspondiente se envía como un objeto de tipoTIMESTAMPa la base de datos. El formato aceptado esYYYY-MM-DD HH:MM:SS[.FFF]. -
typeHint.UUID: el valor del parámetro de cadena correspondiente se envía como un objeto de tipoUUIDa la base de datos.