Referencia de plantillas de mapeo de solucionador de AWS AppSync para Lambda
nota
Ahora admitimos de forma básica el tiempo de ejecución APPSYNC_JS y su documentación. Considere la opción de utilizar el tiempo de ejecución APPSYNC_JS y sus guías aquí.
Puede usar las funciones y los solucionadores de AWS AppSync para invocar las funciones de Lambda ubicadas en su cuenta. Puede configurar las cargas útiles de sus solicitudes y la respuesta de sus funciones de Lambda antes de devolverlas a sus clientes. También puede usar las plantillas de mapeo para indicar a AWS AppSync la naturaleza de la operación que se va a invocar. En esta sección se describen las distintas plantillas de mapeo para las operaciones de Lambda admitidas.
Plantilla de mapeo de solicitudes
La plantilla de mapeo de solicitudes de Lambda gestiona los campos relacionados con la función de Lambda:
{ "version": string, "operation": Invoke|BatchInvoke, "payload": any type, "invocationType": RequestResponse|Event }
A continuación, se muestra un ejemplo de esquema JSON de la plantilla de mapeo de solicitudes de Lambda una vez resuelta:
{ "definitions": {}, "$schema": "https://json-schema.org/draft-06/schema#", "$id": "https://aws.amazon.com/appsync/request-mapping-template.json", "type": "object", "properties": { "version": { "$id": "/properties/version", "type": "string", "enum": [ "2018-05-29" ], "title": "The Mapping template version.", "default": "2018-05-29" }, "operation": { "$id": "/properties/operation", "type": "string", "enum": [ "Invoke", "BatchInvoke" ], "title": "The Mapping template operation.", "description": "What operation to execute.", "default": "Invoke" }, "payload": {}, "invocationType": { "$id": "/properties/invocationType", "type": "string", "enum": [ "RequestResponse", "Event" ], "title": "The Mapping template invocation type.", "description": "What invocation type to execute.", "default": "RequestResponse" } }, "required": [ "version", "operation" ], "additionalProperties": false }
A continuación, se muestra un ejemplo en el que se usa una operación invoke cuyos datos de carga útil son el campo getPost de un esquema de GraphQL junto con sus argumentos del contexto:
{ "version": "2018-05-29", "operation": "Invoke", "payload": { "field": "getPost", "arguments": $util.toJson($context.arguments) } }
Todo el documento de mapeo se transfiere como la entrada a la función de Lambda, de modo que el ejemplo anterior tendría ahora el siguiente aspecto:
{ "version": "2018-05-29", "operation": "Invoke", "payload": { "field": "getPost", "arguments": { "id": "postId1" } } }
Versión
El campo version es común a todas las plantillas de mapeo de solicitudes y define la versión que utiliza la plantilla. El campo version es obligatorio y tiene asociado un valor estático:
"version": "2018-05-29"
Operación
El origen de datos de Lambda permite definir dos operaciones en el campo operation: Invoke y BatchInvoke. La operación Invoke hace que AWS AppSync llame a la función de Lambda para cada solucionador de campo de GraphQL. BatchInvoke indica a AWS AppSync que realice solicitudes en lotes para el campo de GraphQL actual. El campo operation es obligatorio.
Para Invoke, la plantilla de mapeo de solicitudes resuelta coincide con la carga útil de entrada de la función de Lambda. Modifiquemos el ejemplo anterior:
{ "version": "2018-05-29", "operation": "Invoke", "payload": { "arguments": $util.toJson($context.arguments) } }
Esto se resuelve y se transfiere a la función de Lambda, que podría tener un aspecto similar al siguiente:
{ "version": "2018-05-29", "operation": "Invoke", "payload": { "arguments": { "id": "postId1" } } }
Para BatchInvoke, la plantilla de mapeo se aplica a cada solucionador de campo del lote. Por motivos de concisión, AWS AppSync combina todos los valores payload de la plantilla de mapeo resuelta en una lista bajo un solo objeto que coincide con la plantilla de mapeo. La siguiente plantilla de ejemplo muestra esta combinación:
{ "version": "2018-05-29", "operation": "BatchInvoke", "payload": $util.toJson($context) }
Esta plantilla se resuelve para dar el siguiente documento de mapeo:
{ "version": "2018-05-29", "operation": "BatchInvoke", "payload": [ {...}, // context for batch item 1 {...}, // context for batch item 2 {...} // context for batch item 3 ] }
Cada elemento de la lista payload se corresponde con cada elemento individual del lote. También se espera que la función de Lambda devuelva una respuesta en forma de lista que coincida con el orden de los elementos enviados en la solicitud:
[ { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 1 { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 2 { "data": {...}, "errorMessage": null, "errorType": null } // result for batch item 3 ]
Carga
El campo payload es un contenedor que se utiliza para transferir cualquier formato JSON correcto a la función de Lambda. Si el campo operation se establece en BatchInvoke, AWS AppSync agrupará los valores payload existentes en una lista. El campo payload es opcional.
Tipos de invocación
El origen de datos de Lambda permite definir dos tipos de invocación: RequestResponse y Event. Los tipos de invocación son sinónimos de los tipos de invocación definidos en la API de Lambda. El tipo de invocación RequestResponse permite a AWS AppSync llamar a la función de Lambda de forma sincrónica para esperar una respuesta. La invocación Event permite invocar su función de Lambda de forma asíncrona. Para obtener más información sobre cómo Lambda gestiona las solicitudes de tipo de invocación Event, consulte Invocación asíncrona. El campo invocationType es opcional. Si este campo no se incluye en la solicitud, AWS AppSync utilizará de forma predeterminada el tipo de invocación RequestResponse.
Para cualquier campo invocationType, la solicitud resuelta coincide con la carga útil de entrada de la función de Lambda. Modifiquemos el ejemplo anterior:
{ "version": "2018-05-29", "operation": "Invoke", "invocationType": "Event" "payload": { "arguments": $util.toJson($context.arguments) } }
Esto se resuelve y se transfiere a la función de Lambda, que podría tener un aspecto similar al siguiente:
{ "version": "2018-05-29", "operation": "Invoke", "invocationType": "Event", "payload": { "arguments": { "id": "postId1" } } }
Cuando la operación BatchInvoke se usa junto con el campo de tipo de invocación Event, AWS AppSync combina el solucionador de campos de la misma manera que se mencionó anteriormente, y la solicitud se transfiere a la función de Lambda como un evento asíncrono, con la payload como lista de valores. Le recomendamos que deshabilite el almacenamiento en caché de los solucionadores para los solucionadores de tipo de invocación Event, ya que estos no se enviarían a Lambda si hubiera un acierto de caché.
Plantilla de mapeo de respuestas
Al igual que ocurre con otros orígenes de datos, la función de Lambda envía una respuesta a AWS AppSync que debe convertirse en un tipo de GraphQL.
El resultado de la función de Lambda se define con el objeto context que está disponible a través de la propiedad $context.result de Velocity Template Language (VTL).
Si la forma de la respuesta de la función Lambda coincide exactamente con la forma del tipo de GraphQL, puede reenviar la respuesta mediante la siguiente plantilla de mapeo de respuesta:
$util.toJson($context.result)
No hay campos obligatorios ni restricciones de forma aplicables a la plantilla de mapeo de respuesta. Sin embargo, dado que los tipos de GraphQL son estrictos, la plantilla de mapeo resuelta debe coincidir con el tipo de GraphQL previsto.
Repuesta de la función de Lambda en lotes
Si el campo operation se establece en BatchInvoke, AWS AppSync espera que la función de Lambda devuelva una lista de elementos. Para que AWS AppSync pueda mapear cada resultado a cada elemento de la solicitud original, la lista de respuestas debe coincidir en tamaño y orden. Se pueden tener elementos null en la lista de respuestas; $ctx.result se establece en null en consecuencia.
Solucionadores de Lambda directos
Si desea eludir por completo el uso de plantillas de mapeo, AWS AppSync puede proporcionar una carga útil predeterminada a la función de Lambda y una respuesta predeterminada de una función de Lambda a un tipo de GraphQL. Puede optar por proporcionar una plantilla de solicitud, una plantilla de respuesta o ninguna de las dos, y AWS AppSync se ocupará de su gestión en consecuencia.
Plantilla de mapeo de la solicitud Lambda directa
Si no se proporciona la plantilla de mapeo de solicitudes, AWS AppSync enviará el objeto Context directamente a la función de Lambda como una operación Invoke. Para obtener más información sobre la estructura del objeto Context, consulte Referencia de contexto de las plantillas de mapeo del solucionador de AWS AppSync.
Plantilla de mapeo de la respuesta de Lambda directa
Si no se proporciona la plantilla de mapeo de respuestas, AWS AppSync realizará una de estas dos acciones al recibir la respuesta de la función de Lambda. Si no ha proporcionado una plantilla de mapeo de solicitudes o si ha proporcionado una plantilla de mapeo de solicitudes con la versión 2018-05-29, respuesta será equivalente a la siguiente plantilla de mapeo de respuestas:
#if($ctx.error) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #end $util.toJson($ctx.result)
Si ha proporcionado una plantilla con la versión 2017-02-28, la lógica de respuesta funcionará de manera equivalente a la siguiente plantilla de mapeo de respuestas:
$util.toJson($ctx.result)
Superficialmente, la omisión de plantillas de mapeo funciona de manera similar a utilizar ciertas plantillas de mapeo, como se muestra en los ejemplos anteriores. Sin embargo, entre bastidores, se elude por completo la evaluación de las plantillas de mapeo. Al omitirse el paso de evaluación de la plantilla, las aplicaciones podrían experimentar en algunas situaciones menos sobrecarga y latencia durante la respuesta en comparación con una función de Lambda con una plantilla de mapeo de respuestas que debe evaluarse.
Gestión de errores personalizada en las respuestas de solucionador de Lambda directo
Puede personalizar las respuestas de error de las funciones de Lambda que invocan los solucionadores de Lambda directos mostrando una excepción personalizada. En el siguiente ejemplo se muestra cómo crear una excepción personalizada mediante JavaScript:
class CustomException extends Error { constructor(message) { super(message); this.name = "CustomException"; } } throw new CustomException("Custom message");
Cuando se muestran excepciones, errorType y errorMessage son el name y message, respectivamente, del error personalizado que se produce.
Si errorType es UnauthorizedException, AWS AppSync devuelve el mensaje predeterminado ("You are not authorized to make this
call.") en lugar de uno personalizado.
El siguiente fragmento es un ejemplo de respuesta de GraphQL que muestra un errorType personalizado:
{ "data": { "query": null }, "errors": [ { "path": [ "query" ], "data": null, "errorType": "CustomException", "errorInfo": null, "locations": [ { "line": 5, "column": 10, "sourceName": null } ], "message": "Custom Message" } ] }
Solucionadores de Lambda directos: agrupación en lotes habilitada
Puede habilitar la agrupación en lotes para el solucionador de Lambda directo mediante la configuración del maxBatchSize en el solucionador. Cuando maxBatchSize se establece en un valor superior a 0 para un solucionador de Lambda directo, AWS AppSync envía solicitudes en lotes a la función de Lambda en el tamaño de lote máximo que indique maxBatchSize.
Si maxBatchSize se establece en 0 en un solucionador de Lambda directo, se desactivará la agrupación en lotes.
Para obtener más información sobre el funcionamiento de la agrupación en lotes con solucionadores de Lambda, consulte Caso de uso avanzado: agrupación en lotes.
Plantilla de mapeo de solicitudes
Si la agrupación en lotes está habilitada y no se proporciona la plantilla de mapeo de solicitudes, AWS AppSync envía una lista de objetos Context como una operación BatchInvoke directamente a la función de Lambda.
Plantilla de mapeo de respuestas
Si la agrupación en lotes está habilitada y no se proporciona la plantilla de mapeo de respuestas, la lógica de respuesta es equivalente a la siguiente plantilla de mapeo de respuestas:
#if( $context.result && $context.result.errorMessage ) $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data) #else $utils.toJson($context.result.data) #end
La función de Lambda debe devolver una lista de resultados en el mismo orden que la lista de objetos Context que se han enviado. Puede devolver errores individuales proporcionando un errorMessage y errorType para un resultado específico. Cada resultado de la lista se indica con el formato siguiente:
{ "data" : { ... }, // your data "errorMessage" : { ... }, // optional, if included an error entry is added to the "errors" object in the AppSync response "errorType" : { ... } // optional, the error type }
nota
Actualmente, se ignoran otros campos del objeto de resultado.
Gestión de errores de Lambda
Puede devolver un error para todos los resultados produciendo una excepción o un error en la función de Lambda. Si el tamaño de respuesta o solicitud de carga de la solicitud por lote es demasiado grande, Lambda devolverá un error. En ese caso, debería considerar la posibilidad de reducir el maxBatchSize o el tamaño de la carga de la respuesta.
Para obtener información sobre la gestión de errores individuales, consulte Devolución de errores individuales.
Funciones de Lambda de ejemplo
Con el siguiente esquema, puede crear un solucionador de Lambda directo para el solucionador de campo Post.relatedPosts y habilitar la agrupación en lotes al establecer maxBatchSize por encima de 0:
schema { query: Query mutation: Mutation } type Query { getPost(id:ID!): Post allPosts: [Post] } type Mutation { addPost(id: ID!, author: String!, title: String, content: String, url: String): Post! } type Post { id: ID! author: String! title: String content: String url: String ups: Int downs: Int relatedPosts: [Post] }
En la siguiente consulta, se llamará a la función de Lambda con lotes de solicitudes para resolver relatedPosts:
query getAllPosts { allPosts { id relatedPosts { id } } }
A continuación, se proporciona una implementación sencilla de una función de Lambda:
const posts = { 1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', }, 2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', }, 3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null }, 4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', }, 5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', }, } const relatedPosts = { 1: [posts['4']], 2: [posts['3'], posts['5']], 3: [posts['2'], posts['1']], 4: [posts['2'], posts['1']], 5: [], } exports.handler = async (event) => { console.log('event ->', event) // retrieve the ID of each post const ids = event.map((context) => context.source.id) // fetch the related posts for each post id const related = ids.map((id) => relatedPosts[id]) // return the related posts; or an error if none were found return related.map((r) => { if (r.length > 0) { return { data: r } } else { return { data: null, errorMessage: 'Not found', errorType: 'ERROR' } } }) }