Introducción a AWS

Crear una aplicación web sin servidor

con AWS Lambda, Amazon API Gateway, AWS Amplify, Amazon DynamoDB y Amazon Cognito

Módulo 3: backend de servicio sin servidor

Utilizará AWS Lambda y Amazon DynamoDB para crear un proceso de backend destinado a administrar las solicitudes de la aplicación web

Información general

En este módulo, utilizará AWS Lambda y Amazon DynamoDB para crear un proceso de backend destinado a administrar las solicitudes de la aplicación web. La aplicación de navegador que implementó en el primer módulo permite a los usuarios solicitar el envío de un unicornio a una ubicación de su elección. Para responder a esas solicitudes, el código JavaScript que se ejecuta en el navegador deberá invocar un servicio que se ejecuta en la nube.

Información general acerca de la arquitectura

Información general acerca de la arquitectura

Implementará una función de Lambda que se invocará cada vez que un usuario solicite un unicornio. La función seleccionará un unicornio de la flota, registrará la solicitud en una tabla de DynamoDB y, después, responderá a la aplicación de frontend con detalles acerca del unicornio que se envía.

La función se invoca desde el navegador con Amazon API Gateway. Implementará dicha conexión en el siguiente módulo. Para este módulo, simplemente probará su función de forma aislada.

 Tiempo de realización

30 minutos

 Servicios utilizados

Implementación

  • Utilice la consola de Amazon DynamoDB para crear una nueva tabla de DynamoDB. 

    1. En la consola de Amazon DynamoDB, haga clic en Crear tabla.
    2. En Nombre de la tabla, ingrese Rides. Este campo distingue entre mayúsculas y minúsculas.
    3. Para Clave de partición, ingrese RideId y seleccione Cadena como tipo de clave. Este campo distingue entre mayúsculas y minúsculas.
    4. En la sección Configuración de la tabla, asegúrese de que esté seleccionada la opción Configuración predeterminada y elija Crear tabla
    5. En la página Tablas, espere a que se complete la creación de la tabla. Una vez que se complete, el estado será Activo. Seleccione el nombre de la tabla.
    6. En la pestaña Descripción general > sección Información general de la nueva tabla, seleccione Información adicional. Copie el ARN. Lo usará en la sección siguiente.
  • Cada función de Lambda tiene un rol de IAM asociado. Este rol define con qué otros servicios de AWS se permite interactuar a la función. Para este tutorial, deberá crear un rol de IAM que conceda a su función de Lambda permisos de escritura de registros en Registros de Amazon CloudWatch y acceso de escritura de elementos en su tabla de DynamoDB.

    1. En la consola de IAM, seleccione Roles en el panel de navegación izquierdo y, a continuación, elija Crear rol.
    2. En la sección Tipo de entidad de confianza, seleccione Servicio de AWS. En Caso de uso, seleccione Lambda y, a continuación, elija Siguiente
      Nota: Al seleccionar un tipo de rol, se crea automáticamente una política de confianza para este que permite a los servicios de AWS asumir este rol en su nombre. Si está creando este rol con la CLI, AWS CloudFormation u otro mecanismo, especifique una política de confianza directamente.
    3. Ingrese AWSLambdaBasicExecutionRole en el cuadro de texto del filtro y pulse Intro
    4. Seleccione la casilla de verificación situada junto al nombre de la política AWSLambdaBasicExecutionRole y elija Siguiente.
    5. Ingrese WildRydesLambda para Nombre de rol. Mantenga la configuración predeterminada para los demás parámetros.
    6. Elija Crear rol.
    7. En el cuadro de filtro de la página Roles, escriba WildRydesLambda y seleccione el nombre del rol que acaba de crear.
    8. En la pestaña Permisos, elija Agregar permisos y, luego, elija Crear política insertada.
    9. En la sección Seleccione un servicio, escriba DynamoDB en la barra de búsqueda y seleccione DynamoDB cuando aparezca.
    10. Elija Seleccionar acciones.
    11. En la sección Acciones permitidas, escriba PutItem en la barra de búsqueda y seleccione la casilla de verificación situada junto a PutItem cuando aparezca.
    12. En la sección Recursos, con la opción Específica seleccionada, elija el enlace Agregar ARN.
    13. Seleccione la pestaña Texto. Pegue el ARN de la tabla que creó en DynamoDB (paso 6 de la sección anterior) y elija Agregar ARN.
    14. Seleccione Siguiente.
    15. Escriba DynamoDBWriteAccess como nombre de la política y elija Crear política.
  • AWS Lambda ejecutará su código como respuesta a eventos tales como una solicitud HTTP. En este paso, vamos a crear la función principal que procesará las solicitudes API de la aplicación web para enviar un objeto Unicorn. En el módulo siguiente utilizaremos Amazon API Gateway a fin de crear una API RESTful para exponer un punto de enlace HTTP que se puede invocar desde los navegadores de los usuarios. Después, conectará a dicha API la función de Lambda que haya creado en este paso, a fin de crear un backend plenamente funcional para la aplicación web.

    Use la consola de AWS Lambda para crear una función de Lambda nueva llamada RequestUnicorn que procesará las solicitudes de la API. Use la implementación de ejemplo requestUnicorn.js siguiente para el código de la función. Solo tiene que copiar y pegar desde dicho archivo al editor de la consola de AWS Lambda.

    Asegúrese de configurar la función para poder usar el rol de IAM WildRydesLambda que creó en la sección anterior.

    1. En la consola de AWS Lambda, elija Crear función.
    2. Mantenga seleccionada la tarjeta predeterminada Crear de cero.
    3. Escriba RequestUnicorn en el campo Nombre de función.
    4. Seleccione Node.js 16.x para Tiempo de ejecución (las versiones más recientes de Node.js no funcionarán en este tutorial).
    5. Seleccione Usar un rol existente en el menú desplegable Cambiar el rol de ejecución predeterminado.
    6. Seleccione WildRydesLambda en el menú desplegable Rol existente.
    7. Haga clic en Crear función.
    8. Desplácese hacia abajo hasta la sección Origen de código y reemplace el código existente en el editor de código index.js por el contenido de requestUnicorn.js. El siguiente bloque de código muestra el archivo requestUnicorn.js. Copie y pegue este código en la pestaña index.js del editor de código.
    const randomBytes = require('crypto').randomBytes;
    const AWS = require('aws-sdk');
    const ddb = new AWS.DynamoDB.DocumentClient();
    
    const fleet = [
        {
            Name: 'Angel',
            Color: 'White',
            Gender: 'Female',
        },
        {
            Name: 'Gil',
            Color: 'White',
            Gender: 'Male',
        },
        {
            Name: 'Rocinante',
            Color: 'Yellow',
            Gender: 'Female',
        },
    ];
    
    exports.handler = (event, context, callback) => {
        if (!event.requestContext.authorizer) {
          errorResponse('Authorization not configured', context.awsRequestId, callback);
          return;
        }
    
        const rideId = toUrlString(randomBytes(16));
        console.log('Received event (', rideId, '): ', event);
    
        // Because we're using a Cognito User Pools authorizer, all of the claims
        // included in the authentication token are provided in the request context.
        // This includes the username as well as other attributes.
        const username = event.requestContext.authorizer.claims['cognito:username'];
    
        // The body field of the event in a proxy integration is a raw string.
        // In order to extract meaningful values, we need to first parse this string
        // into an object. A more robust implementation might inspect the Content-Type
        // header first and use a different parsing strategy based on that value.
        const requestBody = JSON.parse(event.body);
    
        const pickupLocation = requestBody.PickupLocation;
    
        const unicorn = findUnicorn(pickupLocation);
    
        recordRide(rideId, username, unicorn).then(() => {
            // You can use the callback function to provide a return value from your Node.js
            // Lambda functions. The first parameter is used for failed invocations. The
            // second parameter specifies the result data of the invocation.
    
            // Because this Lambda function is called by an API Gateway proxy integration
            // the result object must use the following structure.
            callback(null, {
                statusCode: 201,
                body: JSON.stringify({
                    RideId: rideId,
                    Unicorn: unicorn,
                    Eta: '30 seconds',
                    Rider: username,
                }),
                headers: {
                    'Access-Control-Allow-Origin': '*',
                },
            });
        }).catch((err) => {
            console.error(err);
    
            // If there is an error during processing, catch it and return
            // from the Lambda function successfully. Specify a 500 HTTP status
            // code and provide an error message in the body. This will provide a
            // more meaningful error response to the end client.
            errorResponse(err.message, context.awsRequestId, callback)
        });
    };
    
    // This is where you would implement logic to find the optimal unicorn for
    // this ride (possibly invoking another Lambda function as a microservice.)
    // For simplicity, we'll just pick a unicorn at random.
    function findUnicorn(pickupLocation) {
        console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude);
        return fleet[Math.floor(Math.random() * fleet.length)];
    }
    
    function recordRide(rideId, username, unicorn) {
        return ddb.put({
            TableName: 'Rides',
            Item: {
                RideId: rideId,
                User: username,
                Unicorn: unicorn,
                RequestTime: new Date().toISOString(),
            },
        }).promise();
    }
    
    function toUrlString(buffer) {
        return buffer.toString('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=/g, '');
    }
    
    function errorResponse(errorMessage, awsRequestId, callback) {
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          Error: errorMessage,
          Reference: awsRequestId,
        }),
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
      });
    }

        9. Elija Implementar.

  • En este módulo, probará la función que ha creado mediante la consola de AWS Lambda. En el módulo siguiente, agregará una API de REST con API Gateway para poder invocar su función desde la aplicación basada en navegador que implementó en el primer módulo.

    1. En la función RequestUnicorn que creó en la sección anterior, elija Probar en la sección Origen de código y seleccione Configurar evento de prueba en el menú desplegable.
    2. Mantenga la selección predeterminada Crear nuevo evento.
    3. Escriba TestRequestEvent en el campo Nombre del evento.
    4. Copie y pegue el siguiente evento de prueba en la sección Evento JSON:
    {
        "path": "/ride",
        "httpMethod": "POST",
        "headers": {
            "Accept": "*/*",
            "Authorization": "eyJraWQiOiJLTzRVMWZs",
            "content-type": "application/json; charset=UTF-8"
        },
        "queryStringParameters": null,
        "pathParameters": null,
        "requestContext": {
            "authorizer": {
                "claims": {
                    "cognito:username": "the_username"
                }
            }
        },
        "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
    }

        5. Elija Save (Guardar).

        6. En la sección Origen de código de la función, elija Probar y seleccione TestRequestEvent en el menú desplegable.

        7.  En la pestaña Probar, elija Probar.

        8. En el mensaje Ejecución de función: correcta que aparece, expanda el menú desplegable Detalles.

        9. Compruebe que el resultado de la función sea similar al siguiente:

    {
        "statusCode": 201,
        "body": "{\"RideId\":\"SvLnijIAtg6inAFUBRT+Fg==\",\"Unicorn\":{\"Name\":\"Rocinante\",\"Color\":\"Yellow\",\"Gender\":\"Female\"},\"Eta\":\"30 seconds\"}",
        "headers": {
            "Access-Control-Allow-Origin": "*"
        }
    }

¿Le resultó útil esta página?

Implementar una API RESTful