SAM y AWS: Crear una API

sam

Crear una API con AWS y SAM

En esta publicación, vamos a crear una nueva aplicación que consta de una API que tiene un punto simple desarrollado en NodeJs y tiene un depósito S3. Toda la infraestructura de este proyecto se creará automáticamente utilizando SAM (AWS Serverless Application Model).


Preparar el ambiente
El primer paso es preparar el entorno de desarrollo instalando AWS CLI, SAM y NodeJs.

Para instalar la CLI de AWS simplemente siga los pasos explicados en el documento de AWS: Instalación o actualización de la última versión de la CLI de AWS. Confirme que lo ha instalado correctamente ejecutando:

> aws --version
aws-cli/2.7.19 Python/3.9.11 Windows/10 exe/AMD64 prompt/off

Luego, para instalar AWS SAM sigamos los pasos explicados en el documento de AWS: Installing the AWS SAM CLI. Confirme que lo ha instalado correctamente ejecutando:

> sam --version
SAM CLI, version 1.53.0

La instalación de Docker es opcional para SAM en este caso, pero es necesaria para probar algunos recursos de AWS. Por ejemplo, para ejecutar un Lambda localmente, necesitará Docker instalado.

Para las credenciales, crearemos una cuenta de AWS con access_keysecret_access_key, si no tiene estas credenciales, siga esta guía y cree un nuevo par de claves.


Finalmente, configuremos estas credenciales en nuestro entorno de desarrollo ejecutando:

> aws configure
AWS Access Key ID [None]: your_access_key_id AWS
Secret Access Key [None]: your_secret_access_key
Default region name [None]: us-east-1
Default output format [None]: json

Puede encontrar más información sobre cómo configurar las credenciales aquí. las credenciales.

Las credenciales se pueden encontrar en este archivo en el perfil predeterminado.

Codificando el proyecto

Ahora, comencemos con el código. Para este ejemplo vamos a usar NodeJS 16 pero el código de Lambda será tan simple que no va a importar mucho. Comience creando un nuevo proyecto con npm init.

Dentro del proyecto creamos una nueva carpeta donde vamos a almacenar la función, que es realmente simple:

exports.handler = async (event, context) => {
   return {
     statusCode: 200,
     body: JSON.stringify({response: 'Success'})
   }
}

En esta carpeta también es necesario agregar otro archivo package.json con el archivo Lambda como propiedad principal.


Ahora agreguemos el archivo template.yaml que debe estar en la raíz, fuera de la carpeta de Lambda. La plantilla se verá así:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "AWS - Simple API with SAM"

Resources:
  ### Role ###
  LambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
        - PolicyName: "AccessS3Bucket"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - s3:PutObject
                  - s3:GetObject
                Resource: "*"

  ### Bucket S3 ###
  SimpleBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName:
        Fn::Join: ["-", [Ref: AWS::StackName, "simple-bucket"]]
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - "*"
            AllowedMethods:
              - PUT
            AllowedOrigins:
              - "*"
            Id:
              Fn::Join: ["-", [Ref: AWS::StackName, "simple-bucket-cors-rule"]]
            MaxAge: 1800

  ### Lambda ###
  SimpleLamdbaFunction:
    Type: AWS::Serverless::Function
    DependsOn:
      - LambdaRole
    Properties:
      Handler: app.handler
      CodeUri: ./simple-log
      Runtime: nodejs16.x
      Role:
        Fn::GetAtt: LambdaRole.Arn
      Events:
        HttpPost:
          Type: Api
          Properties:
            Path: '/simple-log'
            Method: post

Los cuatro valores iniciales son:

  • AWSTemplateFormatVersion: la versión de la plantilla de Cloudformation. Actualmente, 2010–09–09 es la última versión y el único valor válido. Más sobre esto aquí.
  • Transform: Se utiliza para identificar la forma de procesar la plantilla. Más sobre esto aquí.
  • Descripción: Un texto simple que describe el proyecto.
  • Recursos: Vamos a declarar todos los recursos e infraestructura que necesitamos. Más sobre esto aquí.

Vamos a crear tres recursos:

  1. El papel de la Lambda. Este rol tiene los conceptos básicos de Lambda y las políticas para colocar y obtener objetos de cualquier depósito de S3 en la pila.
  2. El cubo S3.
  3. La lambda. Aquí definimos la ruta para realizar la solicitud, y la ruta y el nombre de la función a ejecutar. El rol de la función apuntará al ARN (Amazon Resource Names) del rol que definimos anteriormente.

Ahora estamos listos para construir e implementar la pila:

sam build ; sam deploy --guided

Como es la primera vez que implementamos el proyecto, es útil tener la implementación guiada, en cuyo caso tendremos que responder algunas preguntas a través de la línea de comando.

SAM
Preguntas de la implementación guiada

Una vez implementado, verifiquemos que todo se haya creado correctamente. Para probar la lambda, acceda a API Gateway → APIs → <Your api> → Dashboard. Aquí, copie la URL para invocar la API. Me veré así:

https://<hashCode>.execute-api.<region>.amazonaws.com/Prod/

Luego, dentro de Resources encontraremos el nuevo punto final con su ruta y método Http.

SAM
La ruta de lambda y el método HTTP

Ahora, verifiquemos la creación del depósito yendo a la sección S3 de la consola de AWS.

SAM
Ejemplo de la creación del cubo

¡Funcionó perfectamente! Todos los recursos fueron creados y si queremos actualizarlos, todo lo que tenemos que hacer es compilarlo e implementarlo nuevamente.


Enlaces útiles


Si le interesa, puede echar un vistazo a algunos de los otros artículos que he escrito recientemente sobre Laravel:

Recent Post