Skip to content

Microservicio Api Rest para la Gestión de Máquinas Industriales implementando Serverless, Api Gateway, SSM, Bucket S3, NodeJs y Otras Tecnologías.

License

Notifications You must be signed in to change notification settings

andresWeitzel/Api_MaquinasIndustriales_ServerlessBucketS3_NodeJs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Api_MaquinasIndustriales_ServerlessBucketS3_NodeJs

Microservicio Api Rest para la Gestión de Máquinas Industriales implementando Serverless, Api Gateway, SSM, Bucket S3, NodeJs y Otras Tecnologías.


Índice 📜

Ver

Sección 1) Descripción, Tecnologías y Dependencias

Sección 2) Proyecto Serverless desde cero

Sección 3) Amazon Bucket S3



Sección 1) Descripción, Tecnologías y Dependencias

1.0) Descripción 🔝

Ver
  • ipsum

1.1) Ejecución del Proyecto 🔝

Ver
  • Crear un entorno de trabajo a través de algún IDE
  • Clonar el Proyecto ( git clone https://github.com/andresWeitzel/Api_MaquinasIndustriales_ServerlessBucketS3_NodeJs )
  • Dentro del directorio instalar todos los plugins implementados
    • npm install -g serverless
    • npm i serverless-offline
    • npm install serverless-offline serverless-offline-ssm --save-dev
    • npm install serverless-s3-local --save-dev
  • Levantar Serverless en Local (sls offline start)
  • Comprobar respuestas de los endpoints generados a través de alguna herramienta Cliente Http (Ej:Postman)

1.2) Tecnologías 🔝

Ver
Tecnologías Versión Finalidad
SDK 4.3.2 Inyección Automática de Módulas para Lambdas
Serverless Framework Core 3.23.0 Core Servicios AWS
Serverless Plugin 6.2.2 Librerías para la Definición Modular
Systems Manager Parameter Store (SSM) 3.0 Manejo de Variables de Entorno
Amazon Api Gateway 2.0 Gestor, Autenticación, Control y Procesamiento de la Api
Amazon S3 Bucket 3.0 Contenedor de Objetos
NodeJS 14.18.1 Librería JS
VSC 1.72.2 IDE
Postman 10.11 Cliente Http
CMD 10 Símbolo del Sistema para linea de comandos
Git 2.29.1 Control de Versiones

1.3) Plugins 🔝

Ver
Plugin
serverless-offline
serverless-offline-ssm
serverless-s3-local

1.4) Extensiones VSC 🔝

Ver
Extensión
Prettier - Code formatter
YAML - Autoformatter .yml (alt+shift+f)
YAML-fm Linter
DotENV


Sección 2) Proyecto Serverless desde cero

Arquitectura Lambda Function y Api gateway

Ver

2.0) Instalación y Configuración de Serverless Local 🔝

Ver
  • Una vez abierto el proyecto instalamos serverless de forma Global npm install -g serverless

  • Seguidamente creamos toda la config de serverless para nuestro proyecto(en mi caso el nombre del proyecto es api-maquinas-industriales ) serverless create --template aws-nodejs --path api-maquinas-industriales && cd api-maquinas-industriales

  • Luego inicializamos el package.json en el proyecto npm init -y.

  • Instalamos el plugin serverless-offline npm i serverless-offline

  • Comprobamos versión serverless --version

  • Salida Esperada ..

     Framework Core: 3.23.0
     Plugin: 6.2.2
     SDK: 4.3.2
    
  • Agregamos el plugin instalado de serverless-offline al archivo serverless.yml

  • Configuramos los diversos parámetros necesarios del provider

  • Vamos a trabajar todas las lambdas dentro del directorio src/lambdas/ por asuntos de modularización de código

  • Renombramos la lambda handler.js para hello.js

  • Configuramos tipo de método y path a través de httpApi.

  • Configuramos el puerto http

  • Archivo serveless.yml..

        service: api-maquinas-industriales
    
        frameworkVersion: '3'
    
        provider:
          name: aws
          runtime: nodejs14.x
          stage: offline
          region : us-west-1
          memorySize: 512
          timeout : 20
    
        plugins:
          - serverless-offline 
    
        custom: 
          serverless-offline:
            httpPort: 4000 
    
    
        functions:
          hello:
            handler: src/lambdas/hello.msg
            events:
              - httpApi:
                  method: GET
                  path: hello
    
    
  • La lambda hello.js la movemos al directorio ya mencionado src/lambdas/

  • Archivo Lambda hello.js..

     'use strict';
    
      module.exports.msg = async (event) => {
        return {
          statusCode: 200,
          body: JSON.stringify(
            {
              message: 'HELLO',
              input: event,
            },
            null,
            2
          ),
        };
    
      };
    


2.1) Configuración de Api Gateway 🔝

Ver
  • API Gateway gestiona todas las tareas relacionadas con la aceptación y el procesamiento de centenares de miles de llamadas simultáneas a la API. Estas tareas incluyen la administración del tráfico, el control de la autorización y el acceso, el monitoreo y la administración de versiones de la API.

  • No es necesario la instalación de ningún paquete adicional, este servicio viene incluido en la instalación principal de serverless.

  • Para cada lambda es necesario adicionar el parametro private: true dentro de - httpApi para que se aplique la restricción de acceso correctamente.

  • Vamos a generar una sección de resources . Esta es la plantilla de CloudFormation (Servicio de recursos de AWS) para declarar los recursos de serverless a utilizar.

  • En este caso vamos a extender los diversos manejos de recursos para nuestra Api Gateway. (Tipos, Templates y Códigos de Respuesta).

  • La configuración General de nuestro serverless.yml quedaría...

     service: api-maquinas-industriales
    
     frameworkVersion: '3'
    
     provider:
       name: aws
       runtime: nodejs14.x
       stage: offline
       region : us-west-1
       memorySize: 512
       timeout : 20
    
     plugins:
       - serverless-offline 
    
     custom: 
       serverless-offline:
         httpPort: 4000 
    
    
     functions:
       hello:
         handler:  src/lambdas/hello.msg
         events:
           - httpApi:
               method: GET
               path: hello
               private: true
    
    resources:
    Resources:
      ApiGatewayRestApi:
        Type: AWS::ApiGateway::RestApi
        Properties:
          Name: apiGatewayRestApi
      #### Gateway Response INIT
      GatewayResponseDefault400:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: DEFAULT_4XX
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-4XX-generic\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseDefault500:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: DEFAULT_5XX
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-5XX-generic\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseAccessDeied:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: ACCESS_DENIED
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-403-access-denied\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseApiConfigurationError:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: API_CONFIGURATION_ERROR
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-500-api-configuration-error\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseAuthorizerConfigurationError:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: AUTHORIZER_CONFIGURATION_ERROR
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-500-authorizer-configuration-error\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseAuthorizerFailure:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: AUTHORIZER_FAILURE
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-500-authorizer-failure\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseBadRequestBody:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: BAD_REQUEST_BODY
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-400-bad-request-body\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseBadRequestParameters:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: BAD_REQUEST_PARAMETERS
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-400-bad-request-parameters\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseExpiredToken:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: EXPIRED_TOKEN
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-403-expired-token\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseIntegrationFailure:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: INTEGRATION_FAILURE
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-504-integration-failure\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseIntegrationTimeout:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: INTEGRATION_TIMEOUT
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-504-integration-timeout\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseInvalidApiKey:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: INVALID_API_KEY
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-403-invalid-api-key\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseInvalidSignature:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: INVALID_SIGNATURE
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-403-invalid-signature\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseMissingAuthenticationToken:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: MISSING_AUTHENTICATION_TOKEN
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-403-missing-authentication-token\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseQuotaExceeded:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: QUOTA_EXCEEDED
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-429-quota-exceeded\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseRequestTooLarge:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: REQUEST_TOO_LARGE
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-413-request-too-large\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseResourceNotFound:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: RESOURCE_NOT_FOUND
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-404-resource-not-found\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseThrottled:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: THROTTLED
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-429-throttled\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseUnauthorized:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: UNAUTHORIZED
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-401-unauthorized\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      GatewayResponseUnauthorizedMediType:
        Type: 'AWS::ApiGateway::GatewayResponse'
        Properties:
          RestApiId: 
            Ref: 'ApiGatewayRestApi'
          ResponseType: UNSUPPORTED_MEDIA_TYPE
          ResponseTemplates:
            application/json: "{\"error\":{\"code\":\"custom-415-unsupported-media-type\",\"message\":$context.error.messageString},\"requestId\":\"$context.requestId\"}"
      #### Gateway Response END
    
  • Código Base : https://gist.github.com/jonatassaraiva/4c33dd8225605c02318cd71a55b2335d


2.2) Ejecución de Serverless Local 🔝

Ver
  • Por defecto tenemos configurado una lambda llamada hello a través de su función .msg

  • Comprobamos la config generada.

  • Además tenemos configurada la seguridad y manejo de responses por parte de la Api Gateway, esta nos provera un token de acceso (x-api-key) para el acceso a cada lambda.

  • Levantamos serverless con el comando sls offline start o serverless offline start

  • Visualizamos el endpoint local que serverless nos genera..

    Starting Offline at stage dev (us-east-1)
    
    Offline [http for lambda] listening on http://localhost:3002
    Function names exposed for local invocation by aws-sdk:
             * hello: api-maquinas-industriales-offline-hello
    Remember to use 'x-api-key' on the request headers.
    Key with token: 'd41d8cd98f00b204e9800998ecf8427e'           
             
    GET | http://localhost:4000/hello   
    POST | http://localhost:4000/2015-03-31/functions/hello/invocations 
    
    Server ready: http://localhost:4000
  • Abrimos alguna herramienta para generar peticiones http (ej: Postman) y generamos una request al endpoint generado junto con la api key de nuestro gateway

  • Seleccionamos método GET, luego escribimos el endpoint http://localhost:4000/hello, seguidamente pestaña Headers en KEY colocamos x-api-key y en VALUE colocamos el token generado d41d8cd98f00b204e9800998ecf8427e.

  • Procedemos a ejecutar la request y podemos comprobar la metadata de la lambda en la consola de postman

  • También obtenemos la respuesta por consola..

      GET /hello (λ: hello)
     (λ: hello) RequestId: 63fc1719-ae56-4d56-8296-87d45b44fc96  Duration: 124.64 ms  Billed Duration: 125 ms

2.3) SSM e IAM 🔝

2.3.0) Arquitectura Lambda Function y SSM

Ver

2.3.1) Instalación y Configuración de SSM Local 🔝

Ver
  • Instalamos el plugin npm install serverless-offline serverless-offline-ssm --save-dev

  • Agregamos el complemento dentro del serverless.yml. Es importante mantener el orden siguiente (serverless-offline siempre último por temas de compatibilidad).

      plugins:
         - serverless-offline-ssm
         - serverless-offline
  • Dentro del proyecto creamos el archivo serverless.env.yml que será el que contenga los valores de las variables de entorno de nuestro proyecto

  • Dentro de dicho archivo colocamos una variable test para comprobar su funcionamiento..

      HELLO_TEST : ${ssm:/hello_test}  
  • Relacionamos el arhivo serverless.env.yml dentro del serverless.yml agregando el parametro enviroment junto a dicho archivo..

     provider:
       name: aws
       runtime: nodejs14.x
       stage: offline
       region : us-west-1
       memorySize: 512
       timeout : 10
       environment: ${file(serverless.env.yml)}  
  • Seguidamente vamos a configurar el ssm dentro del bloque custom para el archivo serverless.yml

  • Agregamos el stage (IMPORTANTE RESPETAR EL STAGE DEL PROVIDER Y DEL SSM) y la variable test, quedando..

      custom:
       serverless-offline-ssm:
         stages:
           - offline
         ssm:
           '/hello_test': 'HELLO SSM'
       serverless-offline:
         httpPort: 4000  
  • Por último utilizamos dicha variable en la lambda declarada, para invocar una variable de entorno utilizamos process.env.variable.

  • Ejecutar serverless, es recomendable luego de cada instalación y agregado de plugin reiniciar el IDE para que los cambios se produzcan de forma correcta.

      'use strict';
    
      module.exports.hello = async (event) => {
        return {
          statusCode: 200,
          body: JSON.stringify(
            {
              message: process.env.HELLO_TEST,
              input: event,
            },
            null,
            2
          ),
        };
    
      };
  • Ejecutamos el programa, realizamos la petición get a través de postman a nuestra lambda y podemos visualizar la metadata de la misma junto con nuestra variable de entorno.

  • Salida Esperada..

       {
         "message": "HELLO SSM",
         "input": {
             "body": null,
             "cookies": [],
             "headers": {
                 "x-api-key": "d41d8cd98f00b204e9800998ecf8427e",
                 "content-type": "application/json",
                 "user-agent": "PostmanRuntime/7.29.2",
                 "accept": "*/*",
                 "cache-control": "no-cache",
                 "postman-token": "ea2e186f-732f-450e-abf2-4572b3e0206b",
                 "host": "localhost:4000",
                 "accept-encoding": "gzip, deflate, br",
                 "connection": "keep-alive",
                 "content-length": "163"
             },
             "isBase64Encoded": false,
             "pathParameters": null,
             "queryStringParameters": null,
             "rawPath": "/hello",
             "rawQueryString": "",
             "requestContext": {
                 "accountId": "offlineContext_accountId",
                 "apiId": "offlineContext_apiId",
                 "authorizer": {
                     "jwt": {}
                 },
                 "domainName": "offlineContext_domainName",
                 "domainPrefix": "offlineContext_domainPrefix",
                 "http": {
                     "method": "GET",
                     "path": "/hello",
                     "protocol": "HTTP/1.1",
                     "sourceIp": "127.0.0.1",
                     "userAgent": "PostmanRuntime/7.29.2"
                 },
                 "requestId": "offlineContext_resourceId",
                 "routeKey": "GET hello",
                 "stage": "$default",
                 "time": "31/Oct/2022:23:49:02 -0300",
                 "timeEpoch": 1667270942415
             },
             "routeKey": "GET hello",
             "stageVariables": null,
             "version": "2.0"
         }
     }


Sección 3) Amazon Bucket S3

3.0) Arquitectura Lambda Function, Api gateway y Bucket S3 🔝

Ver

3.1) Registro de Objectos Json Bucket S3 🔝

Ver

3.2) Instalación y Configuración de Amazon Bucket S3 🔝

Ver
  • Amazon Simple Storage Service (Amazon S3) es un servicio de almacenamiento de objetos que ofrece escalabilidad, disponibilidad de datos, seguridad y rendimiento líderes en el sector.

  • Vamos a instalar el plugin en local npm install serverless-s3-local --save-dev

  • Agregamos el mismo en la sección plugin del .yml, quedando...

     plugins:
       - serverless-s3-local
       - serverless-offline-ssm
       - serverless-offline 
  • Ejecutar serverless, es recomendable luego de cada instalación y agregado de plugin reiniciar el IDE para que los cambios se produzcan de forma correcta.

  • Seguidamente vamos a declarar una lambda de tipo bucket que nos gestione toda la metadata para las acciones que se realice en tiempo de ejecución.

  • Definimos el path de dicha lambda en el archivo serverless.yml

    functions:
       bucketS3:
         handler : src/lambdas/bucketS3.config
         events:
           - s3: local-bucket
             event: s3:*
  • Definimos la configuración y nombre de dicho bucket

        resources:
          Resources:
            Bucket:
              Type: AWS::S3::Bucket
              Properties:
                BucketName: FILE_UPLOAD_BUCKET
  • Creamos dicha lambda bucketS3.js dentro de src/lambdas/

       //=================== METADATA BUCKET =================
    module.exports.config = (event, context) => {
        console.log(JSON.stringify(event));
        console.log(JSON.stringify(context));
        console.log(JSON.stringify(process.env));
      };
      //=================== FIN METADATA BUCKET =================
  • Ejecutamos serverless y verificamos que no surjan errores.


3.3) Escritura de Objetos 🔝

Ver
  • Los objetos son las entidades de datos en sí, es decir, nuestros archivos. Un object almacena tanto los datos como los metadatos necesarios para S3.

  • Para este ejemplo vamos a modulizar código y abstraernos de las lambdas con respecto a las operaciones de escritura y lectura para el bucket, por ende crearemos funciones que realicen esto.

  • Creamos un nuevo directorio src/utils/bucket.

  • Dentro del mismo el archivo appendBucket.js

  • Crearemos una función que espere como parámetro el tipo de objeto a almacenar y toda la lógica del bucket dentro de esta.

  • Para utilizar la funcionalidad de los buckets es necesario importar el skd de aws, de esta forma podremos instanciar un objeto del tipo S3Client. Para este objeto se le podrán pasar las credenciales necesarias para trabajar en local (S3ERVER) y declarar otros params

  • Seguidamente utilizaremos el método send para indicar el nombre del bucket (Bucket) y su llave (key). Además de indicarle el objeto a almacenar (Body).

      //Imports
      "use strict";
      const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");
    
    
      //======= APPEND BUCKET ===========
      async function put(appendData) {
        try {
    
    
          const client = new S3Client({
            forcePathStyle: true,
            credentials: {
              accessKeyId: "S3RVER", // This specific key is required when working offline
              secretAccessKey: "S3RVER",
            },
            endpoint: "http://localhost:4569",
          });
    
          client.send(
            new PutObjectCommand({
              Bucket: "local-bucket",
              Key: "bucketS3.json",
              Body: await appendData,
            })
          );
        } catch (error) {
          console.log(error);
        }
      }
      //======= END APPEND ===========
    
      module.exports = { put };  
  • Seguidamente configuramos una lambda, es necesario su definición en el serverless.yml

     functions:
       createMachine:
         handler: src/lambdas/createMachine.post
         description: STORING INDUSTRIAL MACHINES IN BUCKET S3.
         events:
           - httpApi:
               method: POST
               path: /dev/machine/
               private: true
  • Crearemos la lambda configurada dentro de src/lambdas/ para que ejecute dicho proceso de escritura del bucket.


About

Microservicio Api Rest para la Gestión de Máquinas Industriales implementando Serverless, Api Gateway, SSM, Bucket S3, NodeJs y Otras Tecnologías.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages