Uso de Aurora Serverless con AWS AppSync - AWS AppSync

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Uso de Aurora Serverless con AWS AppSync

AWS AppSync proporciona una fuente de datos para ejecutar SQL comandos en los clústeres de Amazon Aurora Serverless que se han habilitado con un DataAPI. Puedes usar AppSync resolutores para ejecutar SQL sentencias contra los datos API con consultas, mutaciones y suscripciones de GraphQL.

Crear un clúster

Antes de añadir una fuente de RDS datos, primero AppSync debe habilitar un clúster de Data API on a Aurora Serverless y configurar un secreto mediante AWS Secrets Manager. Puede crear primero un clúster Aurora Serverless con AWS CLI:

aws rds create-db-cluster --db-cluster-identifier http-endpoint-test --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD --engine aurora --engine-mode serverless \ --region us-east-1

Esto devolverá una ARN para el clúster.

Crea un secreto a través de la AWS Secrets Manager consola o también a través de un archivo de entrada como el siguiente utilizando las USERNAME teclas y COMPLEX _ PASSWORD del paso anterior: CLI

{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }

Pase esto como parámetro a AWS CLI:

aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1

Esto devolverá un formulario ARN para el secreto.

Anote el clúster y el secreto ARN de Aurora Serverless para usarlos posteriormente en la AppSync consola al crear una fuente de datos.

Habilite los datos API

Puede habilitar los datos API en su clúster siguiendo las instrucciones de la RDS documentación. Los datos API deben estar habilitados antes de añadirlos como fuente AppSync de datos.

Creación de una base de datos y tabla

Una vez que haya habilitado sus datos, API puede asegurarse de que funcionan con el aws rds-data execute-statement comando de AWS CLI. Esto garantizará que el clúster Aurora Serverless esté configurado correctamente antes de añadirlo al suyo AppSync API. Primero cree una base de datos llamada TESTDBcon el --sql parámetro siguiente:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 --sql "create DATABASE TESTDB"

Si esto se ejecuta sin errores, añada una tabla con el comando de creación de tablas:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"

Si todo ha funcionado sin problemas, puede continuar y agregar el clúster como fuente de datos en su AppSync API.

Esquema de GraphQL

Ahora que sus datos API sin servidor de Aurora están funcionando con una tabla, crearemos un esquema de GraphQL y adjuntaremos resolutores para realizar mutaciones y suscripciones. Cree una nueva API en la AWS AppSync consola, vaya a la página del esquema e introduzca lo siguiente:

type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }

Guarde su esquema y vaya a la página Data Sources (Orígenes de datos) y cree un nuevo origen de datos. Seleccione la Relational database (Base de datos relacional) para el tipo de origen de datos y proporcione un nombre fácil de recordar. Utilice el nombre de la base de datos que creó en el último paso, así como el clúster en el ARN que la creó. Para el rol, puede AppSync crear un nuevo rol o crear uno con una política similar a la siguiente:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:DeleteItems", "rds-data:ExecuteSql", "rds-data:ExecuteStatement", "rds-data:GetItems", "rds-data:InsertItems", "rds-data:UpdateItems" ], "Resource": [ "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster", "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret", "arn:aws:secretsmanager:us-east-1:123456789012:secret:mysecret:*" ] } ] }

Tenga en cuenta que hay dos Statements (Instrucciones) en esta política a las que está concediendo acceso de rol. El primer recurso es su clúster Aurora Serverless y el segundo es su AWS Secrets Manager ARN. Deberá proporcionar la configuración de la fuente BOTHARNsde AppSync datos antes de hacer clic en Crear.

Configuración de solucionadores

Ahora que tenemos un esquema GraphQL válido y una fuente de RDS datos, podemos adjuntar resolutores a los campos de GraphQL de nuestro esquema. Las nuestras API ofrecerán las siguientes capacidades:

  1. crea una mascota mediante la mutación. createPetcampo

  2. actualiza una mascota mediante la mutación. updatePetcampo

  3. eliminar una mascota mediante la mutación. deletePetcampo

  4. consigue una sola mascota a través de la consulta. getPetcampo

  5. enumere todas las mascotas a través de la consulta. listPetscampo

  6. publique las mascotas en un rango de precios a través de la consulta. listPetsByPriceRangecampo

Mutación. createPet

En el editor de esquemas de la AWS AppSync consola, en el lado derecho, selecciona Attach Resolver forcreatePet(input: CreatePetInput!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

#set($id=$utils.autoId()) { "version": "2018-05-29", "statements": [ "insert into Pets VALUES (:ID, :TYPE, :PRICE)", "select * from Pets WHERE id = :ID" ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }

Las SQL sentencias se ejecutarán secuencialmente, según el orden de la matriz de sentencias. Los resultados volverán en el mismo orden. Dado que se trata de una mutación, ejecutamos una instrucción select (selección) después de insert (insertar) para recuperar los valores comprometidos para rellenar la plantilla de mapeo de respuesta de GraphQL.

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Como las sentencias tienen dos SQL consultas, necesitamos especificar el segundo resultado de la matriz que proviene de la base de datos con:$utils.rds.toJsonString($ctx.result))[1][0]).

Mutación. updatePet

En el editor de esquemas de la AWS AppSync consola, en el lado derecho, selecciona Attach Resolver forupdatePet(input: UpdatePetInput!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

{ "version": "2018-05-29", "statements": [ $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"), $util.toJson("select * from Pets WHERE id = :ID") ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Mutación. deletePet

En el editor de esquemas de la AWS AppSync consola, en el lado derecho, selecciona Attach Resolver fordeletePet(input: DeletePetInput!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID"), $util.toJson("delete from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.input.id" } }

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Consulta. getPet

Ahora que se han creado las mutaciones para su esquema, conectaremos las tres consultas para mostrar cómo obtener elementos individuales, listas y aplicar SQL filtros. En el editor de esquemas de la AWS AppSync consola, en la parte derecha, selecciona Attach Resolver forgetPet(id: ID!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.id" } }

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])

Consulta. listPets

En el editor de esquemas de la AWS AppSync consola, en el lado derecho, selecciona Attach Resolver forgetPet(id: ID!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

{ "version": "2018-05-29", "statements": [ "select * from Pets" ] }

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

Consulta. listPetsByPriceRange

En el editor de esquemas de la AWS AppSync consola, en el lado derecho, selecciona Attach Resolver forgetPet(id: ID!): Pet. Elija el origen de los datos de RDS. En la sección Plantilla de mapeo de solicitudes, añada la siguiente plantilla:

{ "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.max), ":MIN": $util.toJson($ctx.args.min) } }

En la sección Plantilla de mapeo de respuestas, añada la siguiente plantilla:

$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])

Ejecutar mutaciones

Ahora que ha configurado todos sus resolutores con SQL sentencias y ha conectado su API GraphQL a su Aurora API Data sin servidor, puede empezar a realizar mutaciones y consultas. En AWS AppSync la consola, selecciona la pestaña Consultas e introduce lo siguiente para crear una mascota:

mutation add { createPet(input : { type:fish, price:10.0 }){ id type price } }

La respuesta debe contener los valores de id, type (tipo) y price (precio) de la siguiente manera:

{ "data": { "createPet": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "type": "fish", "price": "10.0" } } }

Puedes modificar este objeto ejecutando la updatePetmutación:

mutation update { updatePet(input : { id: ID_PLACEHOLDER, type:bird, price:50.0 }){ id type price } }

Ten en cuenta que utilizamos el identificador que devolvió la createPetoperación anterior. Este será un valor único para su registro, ya que el solucionador obtuvo $util.autoId(). Podría eliminar un registro de un modo similar:

mutation delete { deletePet(input : {id:ID_PLACEHOLDER}){ id type price } }

Cree un par de registros con la primera mutación con valores diferentes de price (precio) y, a continuación, ejecute algunas consultas.

Ejecutar consultas

En la pestaña Queries (Consultas) de la consola, utilice la siguiente instrucción para obtener una lista de todos los registros que haya creado:

query allpets { listPets { id type price } }

Esto está bien, pero aprovechemos el SQL WHEREpredicado que teníamos where price > :MIN and price < :MAX en nuestra plantilla de mapeo para Query. listPetsByPriceRangecon la siguiente consulta de GraphQL:

query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }

Solo debe ver registros con un price (precio) superior a 1 USD o inferior a 10 USD. Por último, puede realizar consultas para obtener registros individuales tal y como se indica a continuación:

query onePet { getPet(id:ID_PLACEHOLDER){ id type price } }

Saneamiento de la entrada

Recomendamos que los desarrolladores lo utilicen como protección contra variableMap los ataques SQL de inyección. Si no se utilizan mapas de variables, los desarrolladores son responsables de sanear los argumentos de sus operaciones de GraphQL. Una forma de hacerlo consiste en introducir pasos de validación específicos en la plantilla de mapeo de solicitudes antes de ejecutar una SQL declaración con sus datosAPI. Veamos cómo podemos modificar la plantilla de mapeo de solicitudes del ejemplo de listPetsByPriceRange. En lugar de confiar solamente en la entrada del usuario puede hacer lo siguiente:

#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice)) #set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice)) #if (!$validMaxPrice || !$validMinPrice) $util.error("Provided price input is not valid.") #end { "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.maxPrice), ":MIN": $util.toJson($ctx.args.minPrice) } }

Otra forma de protegerse contra las entradas fraudulentas al ejecutar resoluciones con sus datos API consiste en utilizar sentencias preparadas junto con procedimientos almacenados y entradas parametrizadas. Por ejemplo, en el solucionador para listPets defina el siguiente procedimiento que ejecuta select (seleccionar) como una instrucción preparada:

CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END

Esto se puede crear en su instancia Aurora Serverless utilizando el siguiente comando SQL de ejecución:

aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx" \ --region us-east-1 --database "DB_NAME" \ --sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"

El código de resolución resultante para listPets se ha simplificado, ya que ahora simplemente llamamos al procedimiento almacenado. Como mínimo, toda entrada de cadena debe tener comillas simples con caracteres de escape.

#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type)) #if (!$validType) $util.error("Input for 'type' is not valid.", "ValidationError") #end { "version": "2018-05-29", "statements": [ "CALL listPets(:type)" ] "variableMap": { ":type": $util.toJson($ctx.args.type.replace("'", "''")) } }

Escapar cadenas

Las comillas simples representan el inicio y el final de los literales de cadena de una SQL sentencia, p. ej. 'some string value'. Para permitir el uso de valores de cadena con uno o más caracteres de comillas simples (') dentro de una cadena, cada uno de ellos debe reemplazarse por dos comillas simples (''). Por ejemplo, si la cadena de entrada esNadia's dog, escaparía de ella para SQL una declaración como

update Pets set type='Nadia''s dog' WHERE id='1'