Serverless con Lambda, API Gateway y DynamoDB

Luis Ramirez
6 min readMar 21, 2023

Vamos a construir un backend para un CRUD de tareas haciendo uso de funciones Lambda, API Gateway para crear nuestra API y DynamoDB para guardar las tareas.

Workflow serverless — by Luis G. Ramirez Coronado

Paso 1: Crear la base de datos

  • Debes iniciar sesión en la consola de Lambda: https://console.aws.amazon.com/dynamodbv2
  • Clic en el botón crear tabla
  • Asignamos un nombre a la tabla, en este caso tareas
  • Asignamos una clave de partición que servirá como llave única, en este caso le llamaremos id de tipo numérico.
  • Clic en el botón crear tabla

No nos preocupemos por los demás campos, ya que se agregaran desde la función Lambda para crear una tarea.

Paso 2: Crear las funciones Lambda

Vamos a crear 4 funciones Lambda para las operaciones básicas de un CRUD de tareas.

Las funciones Lambda serán las siguientes:

  • dynamodb-get-tareas: Para obtener todas la tareas registradas
// importamos SDK de AWS y configuramos la región
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { ScanCommand } from "@aws-sdk/lib-dynamodb";

const REGION = "us-east-2";
const client = new DynamoDBClient({ REGION });

export const handler = async(event) => {
// parámetros de la consulta
const params = {
TableName: 'tareas',
}

try{
const data = await client.send(new ScanCommand(params))
const response = {
statusCode: 200,
body: JSON.stringify(data.Items),
};
return response;
}catch(err){
const response = {
statusCode: 200,
body: JSON.stringify('Error => '+err.stack),
};
return response;
}
};
  • dynamodb-create-tarea: Para registrar tarea, desde esta función enviamos los campos adicionales que se desea registrar en la tabla tareas, los campos son el nombre y done (estado de la tarea).
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { PutCommand } from "@aws-sdk/lib-dynamodb";

// Configuracion de region
const REGION = "us-east-2";
const client = new DynamoDBClient({ REGION });

export const handler = async(event) => {
// generando un uuid rudimentario XD
const date = new Date();
const uuid = date.getFullYear()+''+date.getMonth()+''+date.getDate()+''+date.getHours()+''+date.getMinutes()+''+date.getSeconds()+''+date.getMilliseconds();

// obtenemos las parametros
const paramsQuery = event.queryStringParameters;

// parametros para el registro
const params = {
TableName: 'tareas',
Item: {
id: parseInt(uuid),
nombre: paramsQuery.nombre,
done: paramsQuery.done
}
}

try{
// guardamos los datos
const data = await client.send(new PutCommand(params));

const response = {
statusCode: 200,
body: JSON.stringify('Success - Item added'),
};

return response;
}catch(err){
const response = {
statusCode: 500,
body: JSON.stringify('error => ' + err.stack),
};
return response;
}
};
  • dynamodb-update-tarea: Para actualizar los datos de una tarea.
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { UpdateCommand } from "@aws-sdk/lib-dynamodb";

const REGION = "us-east-2";
const client = new DynamoDBClient({ REGION });

export const handler = async(event) => {
// TODO implement
const paramsPath = event.pathParameters;
const paramsQuery = event.queryStringParameters;

const params = {
TableName: 'tareas',
Key: {
id: parseInt(paramsPath.tarea_id),
},
UpdateExpression: "set nombre = :n, done = :d",
ExpressionAttributeValues: {
":n": paramsQuery.nombre,
":d": paramsQuery.done
},
}
try{

await client.send(new UpdateCommand(params));

const response = {
statusCode: 200,
body: JSON.stringify('Success - Item updated'),
};
return response;
}catch(err){
const response = {
statusCode: 500,
body: JSON.stringify('error => '+err.stack),
};

return response;
}
};
  • dynamodb-delete-tarea: Para eliminar una tarea
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DeleteCommand } from "@aws-sdk/lib-dynamodb";

const REGION = "us-east-2";
const client = new DynamoDBClient({ REGION });

export const handler = async(event) => {
// Obtenemos parámetros que se pasan por ruta
const paramsPath = event.pathParameters;

const params = {
TableName: 'tareas',
Key: {
id: parseInt(paramsPath.tarea_id)
}
}

try{
await client.send(new DeleteCommand(params));

const response = {
statusCode: 200,
body: JSON.stringify('Success - Item deleted'),
};

return response;
}catch(err){
const response = {
statusCode: 500,
body: JSON.stringify('error => '+err.stack),
};

return response;
}
};

La documentació que ha servido como guía está en este enlace: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_dynamodb_code_examples.html

En este repositorio estará el código para cada función Lambda: link_repositorio_funciones_lambda

Después de haber creado las funciones Lambda, a cada una de ellas de le debe asignar permisos para poder realizar acciones en la base de datos que se ha creado en DynamoDB. Se debe realizar la siguiente configuración:

  • Seleccionar la función Lambda, y en la pestaña Configuración ir a la opción Permisos.
Image by Luis G. Ramirez Coronado
  • Clic en el rol: dynamodb-gettareas-role-9sujq2vj
  • Debe aparecer el nombre de la política, expandimos la política y clic en el botón editar.
Image by Luis G. Ramirez Coronado
  • En el edito visual de la política, damos clic en Añadir permiso adicionales.
Image by Luis G. Ramirez Coronado
  • Escogemos el servicio al cuál queremos que la función Lambda pueda tener acceso.
Image by Luis G. Ramirez Coronado
  • Buscamos y seleccionamos el servicio de DynamoDB
Image by Luis G. Ramirez Coronado
  • Seleccionamos todas las acciones que vamos a permitir que realice la función Lambda (aunque solo se debe asignar algunas acciones, respetando el principio de privilegio mínimo. Aunque para efectos del caso, asignamos todas las acciones). En el apartado de recursos tambíen asignamos todos las acciones. Por último damos clic en Revisar la política y luego Guardar cambios.
Image by Luis G. Ramirez Coronado

Paso 3: Crear HTTP API

  • Debes iniciar sesión en la consola de API Gateway: https://console.aws.amazon.com/apigateway.
  • Dar clic en el botón crear API
  • API Gateway tiene varias tipos de API (API HTTP, API de WebSocket, API Rest y API Rest privada). Escogemos el tipo API HTTP.
  • Agregamos un nombre a la API, en este caso la llamaremos my-api-tareas y clic en el botón siguiente.
  • En Configuración de rutas, clic en siguiente.
  • En Configuración de etapas, dejamos por defecto las configuraciones y clic en el botón siguiente.
  • Por último clic en el botón crear.

Vamos a crear las siguientes rutas en la opción Develop y en el apartado Routes:

  • /v1/tarea con el método GET, para listar todas la tareas.
  • /v1/tarea con el método POST, para crear una tarea.
  • /v1/tarea/{tarea_id} con el método PUT, para para actulizar una tarea.
  • /v1/tarea/{tarea_id} con el método DELETE, para eliminar una tarea.

A continuación, asociamos a cada ruta su respectiva función Lambda. Para ello vamos al apartado Integrations en Develop.

  • Damos clic en cada ruta, y el botón Agrega integración. Seleccionamos la integración con Lambda y buscamos la función Lambda creada para cada operación CRUD.

Paso 4: Probar CRUD

Ya solo queda probar con un “cliente” el funcionamiento de nuestra API. Usaremos INSOMNIA como cliente para el test.

  • Obtener las tareas
Image by Luis G. Ramirez Coronado
  • Crear tarea
Image by Luis G. Ramirez Coronado
  • Actualizar tarea
Image by Luis G. Ramirez Coronado
  • Eliminar tarea
Image by Luis G. Ramirez Coronado

--

--

Luis Ramirez

Founder STARK & AV | Co-Founder of ANDHEURIS| Software Engineer | Product Manager