Despliegue API Serverless con SAM
Tags: aws, sam, serverless
Published: 2022-11-17 22:06:20


## Intro
Cuando pienso en (IaC) la verdad me vienen a la mente varias herramientas para poder hacerlo, Terraform, Pulumi, CDK he experimentado un poco de todas pero estas ultimas semanas me tope con SAM y debo admitir que aunque no he realizado grandes despliegues tengo un crush con esta herramienta. Que puedo decir es limpio, su estructura de código (YAML) es sencilla y es mucho mas corta a una declaración de CloudFormation por no mencionar que pone a mi disposición varios templates para agilizar el desarrollo, aunque siempre tiene a CouldFormation detrás su curva de aprendizaje es bastante rápida.

- [Requisitos Previos](#requisitos-previos)
- [Como iniciar?](#como-iniciar?)
- [Ejercicio](#ejercicio)
  * [Estructura de archivos](#estructura-de-archivos)
  * [Arquitectura](#arquitectura)
  * [Plantilla](#plantilla)
  * [Funciones Lambda](#funciones-lambda)
  * [Despliegue](#despliegue)
  * [Testing](#testing)
- [Recursos de Referencia](#recursos-de-referencia)

## Requisitos Previos
Para poder utilizar SAM antes de comenzar a desarrollar es necesario lo siguiente:

1. Instalar AWS CLI, siguiendo los siguientes pasos  ([Install](https://docs.aws.amazon.com/es_es/cli/latest/userguide/getting-started-install.html))

2. Verifica que tu instalación fue exitosa ejecutando en la terminal el siguiente comando:
`$ aws --version`

3. Instalar SAM CLI, siguiendo los siguientes pasos ([Install](https://aws.amazon.com/es/serverless/sam/))

4. Verifica que tu instalación fue exitosa ejecutando en la terminal el siguiente comando:
`$ sam --version`

## Como iniciar?
Como ya comente SAM es bastante noble para comenzar a desarrollar y nos ofrece un listado de plantillas que podemos utilizar de base para el tipo de arquitectura que necesitemos, para poder utilizar esta guia puedes utilizar este comando en la terminal:

`$ sam init`

Con esta opción podrás seleccionar una plantilla de aws que ha sido creada utilizando mejores practicas o utilizar una plantilla que hayas creado previamente (muy util cuando ya cuentas con tu equipo de desarrollo y quieres ir creando estándares alrededor de tus aplicaciones serverless).

![options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kjojnn33fhekhtda8akt.png)

Al momento de crear este articulo al escoger la opción 1 podras utilizar cualquiera de estas plantillas:

![Plantillas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/foinqhffph0in8b2ru1t.png)

En este ejercicio no utilizaremos ninguna de estas plantillas pero son bastante utiles para cuando se esta comenzando un proyecto, ademas que tienen ejemplos de unit testing hacia las funciones de lambda algo que en lo personal me gusto mucho.

Algo que tenemos que tener claro de SAM es que cuando lo utilizamos tenemos que tener 2 componentes claves:
1. AWS SAM template specification (plantilla de especificación) en ella se define la infraestructura que necesitamos. Mira: [Detalles de la estructura de la plantilla](https://docs.aws.amazon.com/es_es/serverless-application-model/latest/developerguide/sam-specification.html)
2. AWS SAM command line interface (AWS SAM CLI), el cual utilizaremos para hacer el despliegue de nuestras aplicaciones, mas info, acá: [Referencia de Comandos](https://docs.aws.amazon.com/es_es/serverless-application-model/latest/developerguide/serverless-sam-reference.html#serverless-sam-cli).

## Ejercicio
Dicho esto entremos en materia de nuestro desplieque de una aplicación REST API con SAM, te dejo el link al código [https://github.com/hsaenzG/my-app-with-sam](https://github.com/hsaenzG/my-app-with-sam) que este hecho con la version de SAM 1.60.0, para que puedas jugar con el. Algo importante de mencionar es que con este código podrás desplegar la API, probarla y eliminarla utilizando la capa de datos gratuita de AWS sin ningún costo, solo no olvides borrarla al finalizar utilizando el comando:

`$ sam delete my-api-with-sam` 

### Estructura de archivos
Para este ejercicio necesitaras la siguiente estructura: 

![FileStructure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mkka861oodiq97nm7qab.png)

### Arquitectura
En este ejercicio crearemos la siguiente arquitectura:

![Arquitectura](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gvxb5mh5sjutsqmp2v3i.png)

### Plantilla
Para hacerlo con SAM coloca en el archivo template.yml el siguiente código:

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'

Resources:

  SaveHelloFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: handler.saveHello
      Runtime: nodejs14.x
      CodeUri: ./saludo
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref GreetingsTable
      Environment:
        Variables:
          GREETINGS_TABLE: !Ref GreetingsTable
      Events:
        Api: 
          Type: Api
          Properties:
            Path: /saludo
            Method: POST
  GetHelloFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: handler.getHello
      Runtime: nodejs14.x
      CodeUri: ./saludo
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref GreetingsTable
      Environment:
        Variables:
          GREETINGS_TABLE: !Ref GreetingsTable
      Events:
        HelloAPI:
          Type: Api
          Properties:
            Path: /saludo
            Method: GET

  GreetingsTable:
    Type: AWS::Serverless::SimpleTable

Outputs:
  MyApi:
    Description: "API Gateway endpoint URL"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/saludo/"

```
Acá estamos creado un API Gateway con dos verbos un POST y un GET, cada uno de estos tiene un lambda asociada y estas escriben y leen de una tabla en DynamoDB, ademas que crea una política de permisos de Crear, Modificar y eliminar registros de la tabla de DynamoDB.

En mi caso cree un IAM user en mi cuenta de AWS con únicamente permisos necesarios para la creación de Lambda, DynamoDB, API Gateway y AIM, esta ultima para pueda crear las políticas de seguridad necesarias. Una buena práctica es que asignes únicamente los permisos necesarios a tu usuario de IAM que utilizarás en tus procesos de IaC, esto evitará vulnerabilidades en tu cuenta. 

### Funciones Lambda
En este ejercicio cree ambas funciones en el mismo archivo handler.js pero una buena practica es que crees un archivo diferente para cada una de ellas.

```
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();
const TABLE_NAME = process.env.GREETINGS_TABLE || '';

exports.saveHello = async (event) => {
    console.log(event);
    
    const name = event.queryStringParameters.name;

    const item = {
        id: name,
        name: name,
        date: Date.now()
    }

    console.log(item);

    const savedItem = await saveItem(item);

    return {
        statusCode: 200,
        body: JSON.stringify(savedItem),
      }
}

exports.getHello = async (event) => {
    const name = event.queryStringParameters.name;

    try {
        const item = await getItem(name);
        console.log(item);
    
        if (item.date) {
            const d = new Date(item.date);
            
            return {
                statusCode: 200,
                body: `Usuario creado el:  ${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()}`
            }
        }
    } catch (e) {
        return {
            statusCode: 200,
            body: 'No existe nadie con ese nombre'
        }
    }
}

async function saveItem(item) {
    const params = {
		TableName: TABLE_NAME,
		Item: item
	};

    console.log(params)
    
    return dynamo.put(params).promise().then(() => {
        return item;
    });
};

async function getItem (name) {
    console.log('getItem');
    
    const params = {
      Key: {
        id: name,
      },
      TableName: TABLE_NAME
    };

    console.log(params);
  
    return dynamo.get(params).promise().then(result => {
        console.log(result);
        return result.Item;
    });
};

```
### Despliegue
Y ahora si, para hacer el deploy en tu cuenta únicamente debes ejecutar el comando `$ sam deploy`. Al Finalizar el despliegue podrás visualizar en el terminal el URL de tu API de la siguiente manera:


![URL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2f17gwlc2y6q1bh8s1v.png)

### Testing
Para probarlos únicamente deberas copiar esta URL y utilizar cualquier aplicación para ejecutar http requests, en mi caso utilice Postman, pero puedes utilizar el que prefieras.

POST
![ImagePost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3czfeabjkf2e92kn0i6f.png)

GET

![ImageGet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36m9rwfllfmhjnh3krfe.png)

Listo!.. tienes una REST API Serverless funcional en AWS. **Felicidades!** Y no olvides eliminar toda tu infraestructura al terminar tus pruebas.

## Recursos de Referencia
Les dejo los recursos que yo utilice en la creación de este pequeño ejercicio, por si quisieran indagar un poquito mas en el tema:

[https://docs.aws.amazon.com/es_es/serverless-application-model/index.html](https://docs.aws.amazon.com/es_es/serverless-application-model/index.html)

[https://www.youtube.com/watch?v=k_TQubcn0hM&ab_channel=FooBarServerless](https://www.youtube.com/watch?v=k_TQubcn0hM&ab_channel=FooBarServerless)


Happy Coding!