Abe Estrada

Cloud Cron

Para la realización de un proyecto en el cual estoy utilizando un generador de páginas estáticas llamado GatsbyJS requiero cierta lógica que no puedo realizar con tan solo javascript en el navegador, requiero guardar un archivo con la información para luego consultarla por medio de fetch ahora si dentro de javascript. Para ello es necesario ejecutar una función cada cierto tiempo para tener la información actualizada, para ello estoy utilizando AWS Lambda, AWS S3 y AWS CloudWatch. Este combo me permite ejecutar el código (AWS Lambda) cada cierta cantidad de minutos (AWS CloudWatch) y guardarla temporalmente (AWS S3).

Primero hay que crear una cuenta de AWS, luego ir a la sección IAM (Identity and Access Management) para crear un rol para permitir que Lambda pueda guardar archivos dentro de S3, en otros casos hay que dar permisos para escribir en una base de datos como DynamoDB o tan solo un permiso para poder ejecutar la función que requerimos.

En este caso le vamos a otorgar la póliza AWSLambdaExecute que contiene permisos para ejecutar la función y escribir en S3.

Una vez creado el rol, creamos la función dentro del servicio AWS Lambda, en este caso vamos a darle el nombre de miFuncion, y seleccionar Node.js 6.10 para ejecutarla y el rol existente que previamente creamos lambda_execute.

Luego hau que crear un bucket dentro de AWS S3.

Al cual le podemos agregar la configuración CORS para poder acceder a los archivos desde javascript en el navegador. Este paso puede ser omitido.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Una vez hecho todo esto, podemos empezar a crear el código que vamos a utilizar.

En mi caso me gusta utilizar nvm para poder tener la misma versión de node.js que se va a ejecutar mi funcinón dentro de AWS Lambda.

.nvmrc

v6.10.3

Me gusta también tener instalado lambda-local (npm install -g lambda-local) para poder hacer pruebas locales antes de subir el código a AWS Lambda. Esto agiliza más el proceso de depuración en caso de necesitarlo.

package.json

{
  "name": "miFuncion",
  "description": "Cloud Cron",
  "version": "1.0.0",
  "author": "Abe Estrada",
  "license": "MIT",
  "private": true,
  "scripts": {
    "start": "node index.js",
    "dev": "lambda-local -l index.js -h handler -e examples/index.js -t 5",
    "build": "zip -r function.zip index.js package.json node_modules",
    "deploy": "aws lambda update-function-code --region us-west-2 --function-name miFuncion --zip-file fileb://function.zip",
    "invoke": "aws lambda invoke --region us-west-2 --function-name miFuncion outfile.txt"
  },
  "dependencies": {
    "aws-sdk": "^2.186.0",
    "node-fetch": "^1.7.3"
  },
  "devDependencies": {}
}

Instalamos los archivos necesarios con npm install.

index.js

const fetch = require("node-fetch");
const AWS = require("aws-sdk");

exports.handler = (event, context, callback) => {
  fetch("https://api.coinmarketcap.com/v1/ticker/")
    .then(res => res.json())
    .then(json => {
      const s3 = new AWS.S3();
      s3.putObject({
          Bucket: "mifuncion",
          Key: "current.json",
          Body: JSON.stringify(json),
          ContentType: "application/json",
          ACL: "public-read"
        },
        resp => {
          callback(null, "done");
        }
      );
    });
};

Lo que hace el código es utilizar fetch para bajar el archivo json del API de CoinMarket, luego crear un objecto para S3 llamado current.json con acceso público, una vez que el archivo fue creado llamamos callback para terminar la función. Esto se puede substituir para guardar información en una base de datos, generar otro tipo de archivos o lo que se nos ocurra.

Entonces mi workflow es el siguiente:

Pruebo en mi computadora que la función se ejecute correctamente con npm run dev, si no tengo que hacer cambios, genero el archivo function.zip ejecutando npm run build, luego subo ese archivo a AWS Lambda con npm run deploy, para probar que el código se ejecute correctamente dentro de AWS Lambda invoco la función que ya se encuentra en la “nube” con npm run invoke. Cabe mencionar que para poder ejecutar estos dos últimos comandos es necesario tener instalada la aplicación de línea de comandos de AWS, en macOS con homebrew: brew install aws-cli se puede instalar.

Si todo sale bien, ya se puede crear un “regla” para que la AWS CloudWatch ejecute la función cada cierto tiempo que lo necesitemos.