¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.dev
Sara Rimer 04/04/2022 Cargando comentarios…
Ya son muy conocidos los beneficios de las arquitecturas basadas en microservicios serverless. De hecho, algunos de los principales beneficios de la tecnología serverless son la ausencia de administración de servidores, el escalado flexible o la alta disponibilidad. En este mismo blog, otros compañeros nos cuentan las distintas maneras de trabajar con arquitecturas serverless, la arquitectura Lambda o profundizan en el API Gateway de AWS. Hoy veremos cómo configurar y crear localmente los microservicios para una aplicación que desplegaremos en el servicio AWS Lambda.
Serverless Framework es un proyecto de código abierto para construir e implementar rápidamente aplicaciones sin servidor utilizando servicios en la nube. En este caso vamos a crear una API CRUD básica con nodejs como runtime, DynamoDB como base de datos, declarando las funciones de AWS Lambda y sus activadores a través de una simple sintaxis en YAML, y utilizando un archivo de configuración para la nube.
AWS Lambda es un servicio de Amazon Web Services que permite desplegar y ejecutar código en la nube y que puede ser activada por una función de AWS o por un servicio externo, y DynamoDB es una base de datos NoSQL no administrada.
Lambda es un servicio orientado a eventos, y tiene algunas limitaciones: hasta 15 minutos en la petición y sólo permite un máximo de 1000 ejecuciones concurrentes. Fuera de eso puede ejecutar cualquier lógica de negocio, llamar a otra API, etc.
Te contamos el paso a paso:
NOTA: Pide una tarjeta de crédito, aunque solo cobra si pasamos el nivel de la capa gratuita, explicado en su página, pero que básicamente calcula que no sobrepase:
Es una herramienta para configurar y administrar los productos de AWS, en la página hay abundante documentación, pero para este ejemplo vamos a necesitar:
aws – version
aws configure
Access Key ID:
Secret Access Key:
Region name:
Default output format:
Los 2 primeros son los datos que guardamos al crear el usuario. La región la configuraremos con nuestro código para poder cambiarlo programáticamente si lo necesitamos, y lo dejamos en blanco. Y, el último, tampoco necesitamos completarlo ahora.
En Getting Started indican varias formas de hacerlo, por ejemplo con:
npm install -g serverless
En el directorio donde crearemos el proyecto ejecutamos el comando:
serverless
Permite seleccionar plantillas para el tipo de proyecto, en este caso crear una API:
Descarga una plantilla para el proyecto, y pregunta si queremos registrarnos en Serverless Dashboard y si queremos desplegar ahora.
El proyecto ya está creado.
Serverless Framework ha creado una plantilla que muestra cómo hacer una HTTP API básica con Node.js.
Incluye una función de ejemplo ( handler.js ) que recibe un evento que trae información (cliente, si envía un dato, etc.), y siempre retorna un objeto.
"use strict";
module.exports.hello = async (event) => {
return {
statusCode: 200,
body: JSON.stringify(
{
message: "Go Serverless v2.0! Your function executed
successfully!",
input: event,
},
null,
2
),
};
};}
El framework utiliza serverless.yml donde se detallan las definiciones del servicio, funciones, configuración, recursos, permisos, etc.
service: prueba-serverless-crud
frameworkVersion: '2 || 3'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: '20201221'
region: 'eu-west-3'
functions:
hello:
handler: handler.hello
events:
- httpApi:
path: /
method: get
1. service: referencia del proyecto, suele ser el nombre con el que se desplegará. Si vamos a utilizar varios ervicios es conveniente que cada uno tenga su propio fichero serverless.yml.
2. frameworkVersion: podemos establecer una versión exacta "=" o un rango de versiones ">=".
3. provider: indica el nombre del proveedor y su configuración: versión del lenguaje, memoria (opcional), etc. La configuración y permisos establecidos son heredados por todas las funciones.
4. functions: listado con cada una de las implementaciones (similar a un enrutador de Express).
4.1 Identificador: Nombre de la función.
4.2 handler: clase y método.
4.3 events: tipo de eventos y especificaciones para el tipo de evento.
serverless deploy –verbose
Service Information
service: prueba-serverless-crud
stage: dev
region: eu-west-3
stack: prueba-serverless-crud-dev
resources: 11
api keys:
None
endpoints:
GET - https://xxxxxxxxxx.execute-api.eu-west-3.amazonaws.com/
functions:
hello: prueba-serverless-crud-dev-hello
layers:
None
En AWS-Resources tenemos la documentación completa para definir una tabla de DynamoDB con sus propiedades y atributos.
Para entender el funcionamiento, crearemos una tabla para guardar películas, con los datos del nombre de la película, su director y año de estreno, que en próximos pasos utilizaremos para trabajar con nuestra API.
Configuramos un recurso MoviesTable ( 1 ), de tipo DynamoDB ( 2 ), con el nombre de la tabla MoviesTable ( 3 ), forma de facturación: PAY_PER_REQUEST ( 4 ), definimos los campos, en este caso un id de tipo String ( 5 ), y definimos el campo único ( primary key ) para identificar cada película ( 6 ):
resources:
Resources:
( 1 ) MoviesTable:
( 2 ) Type: AWS::DynamoDB::Table
Properties:
( 3 ) TableName: MoviesTable
( 4 ) BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
( 5 ) - AttributeName: id
AttributeType: S
( 6 ) KeySchema:
- AttributeName: id
KeyType: HASH
serverless deploy –verbose
La terminal muestra todo lo que ha creado de forma automática (ApiGateway, Lambda, IAM, etc), entre otras cosas, la tabla DynamoDB:
CloudFormation - CREATE_IN_PROGRESS - AWS::DynamoDB::Table - MoviesTable
CloudFormation - CREATE_IN_PROGRESS - AWS::DynamoDB::Table - MoviesTable
CloudFormation - UPDATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - UPDATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::DynamoDB::Table – MoviesTable
arn:aws:dynamodb:eu-west-3:xxxxxxxxxxxx:table/MoviesTable
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: '20201221'
region: 'eu-west-3'
( 1 ) iamRoleStatements:
( 2 ) - Effect: Allow
Action:
( 3 ) - dynamodb:*
Resource:
- arn:aws:dynamodb:eu-west-3:xxxxxxxxxxxx:table/MoviesTable
La guía del desarrollador de SDK de AWS detalla la instalación y dependencias necesarias para trabajar con Node.js:
npm install aws-sdk
Para para crear el id de cada nueva entrada de la tabla:
npm install uuid
"dependencies": {
"aws-sdk": "^2.1074.0",
"uuid": "^8.3.2"
}
const { v4 } = require('uuid');
const AWS = require('aws-sdk');
const addMovie = async(event) => {
// Conectar a la Base de datos a través del ClientId
// y el Client Secret ya configurado:
const dynamodb = new AWS.DynamoDB.DocumentClient();
// Recoger los datos provenientes del body de la petición
const { title, director, year } = JSON.parse(event.body);
const createAt = new Date();
const id = v4();
// Crear el objeto para guardar
const newMovie = {
id,
title,
director,
year,
createAt
}
// put permite guardar un dato
// ( no es como el PUT en REST )
await dynamodb.put({
TableName: 'MoviesTable',
Item: newMovie
}).promise()
return {
statusCode: 200,
body: JSON.stringify(newMovie)
}
}
module.exports = {
addMovie
};
createMovie:
handler: src/addMovie.addMovie
events:
- httpApi:
path: /movies
method: post
endpoints:
GET - https://xxxxxxxxxx.execute-api.eu-west-3.amazonaws.com/
POST - https://xxxxxxxxxx.execute-api.eu-west-3.amazonaws.com/movies
De la misma forma, podemos obtener el listado de películas, una película por su id, actualizar una película y borrar una película.
getMovies.js:
const AWS = require('aws-sdk');
const getMovies = async(event) => {
try {
const dynamodb = new AWS.DynamoDB.DocumentClient();
// scan es como hacer un fetch de toda la tabla
const result = await dynamodb.scan({
TableName: 'MoviesTable'
}).promise();
const movies = result.Items;
return {
status: 200,
body: {
movies
}
};
} catch(error) {
console.log(error);
}
}
module.exports = {
getMovies
}
getMovie.js:
const AWS = require('aws-sdk');
const getMovie = async(event) => {
try {
const dynamodb = new AWS.DynamoDB.DocumentClient();
const { id } = event.pathParameters;
// método get para obtener un elemento:
const result = await dynamodb.get({
TableName: 'MoviesTable',
Key: {
id
}
}).promise();
const movie = result.Item;
return {
status: 200,
body: {
movie
}
};
} catch(error) {
console.log(error);
}
}
module.exports = {
getMovie
}
updateMovie.js:
const AWS = require('aws-sdk');
const updateMovie = async(event) => {
try {
const dynamodb = new AWS.DynamoDB.DocumentClient();
// Extraer el id desde los parámetros del path:
const { id } = event.pathParameters;
// Extraer valores recibidos a través del evento:
const { title, director } = JSON.parse(event.body);
const result = await dynamodb.update({
TableName: 'MoviesTable',
// Setear cada uno de los valores
UpdateExpression: 'set title = :title, director = :director',
ExpressionAttributeValues: {
':title': title,
':director': director
},
// Indicar el id que debe modificar:
Key: { id },
// Retornar el valor actual:
ReturnValues: 'ALL_NEW'
}).promise();
return {
status: 200,
body: JSON.stringify({
message: 'Movie updated successfully'
})
};
} catch(error) {
console.log(error);
}
}
module.exports = {
updateMovie,
}
deleteMovie.js:
const AWS = require('aws-sdk');
const deleteMovie = async(event) => {
try {
const dynamodb = new AWS.DynamoDB.DocumentClient();
const { id } = event.pathParameters;
const result = await dynamodb.delete({
TableName: 'MoviesTable',
Key: { id }
}).promise();
return {
status: 200,
body: JSON.stringify({
message: 'Movie deleted successfully'
})
};
} catch(error) {
console.log(error);
}
}
module.exports = {
deleteMovie,
}
service: prueba-serverless-crud
frameworkVersion: '2 || 3'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: '20201221'
region: 'eu-west-3'
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
Resource:
- arn:aws:dynamodb:eu-west-3:xxxxx:table/MoviesTable
functions:
hello:
handler: handler.hello
events:
- httpApi:
path: /
method: get
createMovie:
handler: src/addMovie.addMovie
events:
- httpApi:
path: /movies
method: post
getMovies:
handler: src/getMovies.getMovies
events:
- httpApi:
path: /movies
method: get
getMovie:
handler: src/getMovie.getMovie
events:
- httpApi:
path: /movies/{id}
method: get
updateMovie:
handler: src/updateMovie.updateMovie
events:
- httpApi:
path: /movies/{id}
method: put
deleteMovie:
handler: src/deleteMovie.deleteMovie
events:
- httpApi:
path: /movies/{id}
method: delete
resources:
Resources:
MoviesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: MoviesTable
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
Si tenemos algún error, AWS tiene el servicio CloudWatch:
En la opción Grupos de registros, muestra el listado de todas las funciones creadas, haciendo click en cada una de ellas podemos ver las peticiones realizadas con sus logs.
Con este ejemplo podemos comprobar que Serverless Framework es fácil de implementar para crear una aplicación rápidamente.
También existe la posibilidad de modificar o ampliar la configuración mediante gran cantidad de plugins no utilizados en este ejemplo, aunque existen otras herramientas como AWS CDK, o AWS SAM, que iremos viendo en próximos posts con el objetivo de poder elegir la solución adecuada para cada proyecto. Esperamos que este tutorial os haya sido de utilidad. Y si tienes alguna duda, ¡déjanos un comentario!
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Cuéntanos qué te parece.