Migración de una aplicación MEAN a Arquitectura Serverless y Azure Functions

Glaucia Lemos - Sep 12 '19 - - Dev Community

Este artículo es parte de la colección de contenidos #ServerlessSeptember. Aquí encontrarás todos los artículos publicados durante el mes de septiembre de 2019.

¡Durante este artículo aprenderá cómo migrar de manera simple, rápida y dinámica una aplicación MEAN a una arquitectura Serverless usando Azure Functions!

El proyecto MEAN ya está listo y puedes hacer un git cline o descargarlo AQUI

¡¿Aquí vamos?!

Comprendendo la estructura del proyecto MEAN

En este proyecto nos centraremos en las dos carpetas: api y front. Como la imagen de abajo:

Screen-Shot-09-11-19-at-12-04-PM.png

Si ejecuta esta aplicación, notará que estamos persistiendo en MongoDb y estamos usando Back End, que en este caso estamos usando Node.js.

Los datos persistentes consisten en:

Classe: Funcionario

  • idFuncionario: (number - guid generado por el MongoDb)
  • nomeFuncionario: string
  • cargo: string
  • numeroIdentificador: number

Si desea ejecutar este proyecto localmente, solo siga los pasos en README.md desde el repositorio del proyecto.

Bueno, ahora que tiene el proyecto MEAN en la mano, ¡comencemos a migrar a Azure Functions?

Pero en primer lugar, vamos a entender lo que serían Azure Functions!

¿Qué son Azure Functions? ⚡️

Azure Functions es un servicio de proceso sin servidor que le permite ejecutar código a petición sin necesidad de aprovisionar ni administrar explícitamente la infraestructura.

Y Azure Functions tiene soporte varias lenguajes de programación, que incluyen:

Ya las lenguajes abajo, ya son compatibles, pero están en su versión preview:

  • Bash
  • PHP

Si desea conocer más detalles sobre las lenguajes que tiene soporte a Azure Functions, visite el enlace AQUÍ

Sin embargo, para este artículo nos centraremos en JavaScript. 😉

Plantillas importantes en Azure Functions

Antes de comenzar a migrar, es importante mencionar que Azure Functions tiene varias plantillas listas para usar. Entre ellos:

No entraré en detalles de cada una de las plantillas, de lo contrario este artículo será muy grande. Pero si desea comprender más acerca de cada plantilla y cuál es su mejor uso en una aplicación en particular, le recomiendo leer la documentación AQUÍ.

Para este post, vamos a hacer uso de HTTPTrigger ya que esta plantilla activa la ejecución de su código usando una solicitud HTTP. ¡Y eso es justo lo que necesitaremos!

Si es estudiante en cualquier colegio o universidad, puede crear su cuenta de Azure for Students Azure for Students. Essa conta te dará o benefício em possuir crédito de USD 100,00 para usar os serviços de maneira gratuita, sem necessidade de possuir um cartão de crédito. Para ativar essa conta, bastam acessar o link ao lado: AQUI ¡Con esta cuenta puede hacer uso de 1,000,000 solicitudes gratuitas por mes para procesar eventos en Azure Functions!

Screen-Shot-09-11-19-at-12-42-PM.png

Bueno, después de esta descripción general de Azure Functions, ¡finalmente podemos comenzar nuestra migración! ¡Vamos adelante!

Instalación del paquete Azure Functions Core Tools

Azure Functions Core Tools le permite desarrollar y probar funciones en el equipo local desde el prompt command del sistema o usando el terminal.

Estos son los programas y el paquete que necesitamos para continuar en nuestro tutorial:

Una vez que tenga Node.js instalado en su máquina, simplemente escriba el siguiente comando:

  • Windows
npm install -g azure-functions-core-tools
Enter fullscreen mode Exit fullscreen mode
  • MacOs
brew tap azure/functions
brew install azure-functions-core-tools
Enter fullscreen mode Exit fullscreen mode
  • Linux (Ubuntu/Debian) com APT
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
Enter fullscreen mode Exit fullscreen mode

Para obtener más información para instalar correctamente Azure Functions Core Tools, visite el enlace AQUÍ

¿Y cómo sé si Azure Functions Core Tools están instaladas correctamente? Simplemente escriba el siguiente comando en el terminal:

> func
Enter fullscreen mode Exit fullscreen mode

Si sucede de acuerdo con el siguiente gif, es porque el paquete se instaló correctamente.

gif-serverless-07.gif

Genial! Ahora podemos crear nuestras funciones. Para hacer esto, cree una carpeta local en su máquina y ¡comencemos!

Creando una nueva aplicación en Azure Functions

Ahora que tenemos el paquete instalado, creemos una nueva aplicación. Para hacer esto, solo siga los pasos como el gif:

gif-serverless-08.gif

Tenga en cuenta que cuando abrimos Visual Studio Code, debemos hacer clic en el botón YES que aparece en la esquina inferior derecha para habilitar algunos archivos importantes en el proyecto.

Creando una conexión en MongoDb

Bueno, ahora hagamos algunos cambios necesarios en nuestro proyecto recién creado. Para esto, instalaremos localmente el mongodb en nuestro proyecto. Escriba el siguiente comando:

> npm install mongodb
Enter fullscreen mode Exit fullscreen mode

Al instalar mongoDb en el proyecto, tenga en cuenta que ha habido cambios en el archivo package.json. Al final, el archivo debe ser el siguiente:

  • archivo: package.json
{
  "name": "crud-serverless-mongodb",
  "version": "1.0.0",
  "description": "Projeto azure functions com persistencia com o mongoDb",
  "scripts": {
    "test": "echo \"No tests yet...\""
  },
  "author": "",
  "dependencies": {
    "mongodb": "^3.3.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Ahora creemos una carpeta llamada: shared y dentro crearemos el archivo:mongo.js. La estructura del proyecto ahora se verá de la siguiente manera:

Screen-Shot-09-11-19-at-02-05-PM.png

Ahora cambiemos el archivo mongo.js. Para hacer esto, incluya el siguiente código:

  • archivo: shared/mongo.js
/**
 * Arquivo: mongo.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável por tratar a conexão da Base de Dados localmente
 * Author: Glaucia Lemos
 */

const { MongoClient } = require("mongodb");

const config = {
  url: "mongodb://localhost:27017/crud-serverless-mongodb",
  dbName: "crud-serverless-mongodb"
};

async function createConnection() {
  const connection = await MongoClient.connect(config.url, {
    useNewUrlParser: true
  });
  const db = connection.db(config.dbName);
  return {
    connection,
    db
  };
}

module.exports = createConnection;
Enter fullscreen mode Exit fullscreen mode

¡Aquí estamos creando nuestra conexión local con MongoDb! ¡Muy parecido a lo que ya hacemos en el Back End con Node.js!

Y también cambiemos el archivo local.settings.json. Este archivo es responsable de "almacenar" todas las claves que no queremos exponer al realizar el commit. Tenga en cuenta que este archivo está en la lista de archivos en .gitignore.

Abra el archivo local.settings.json y realice los siguientes cambios:

  • archivo: local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "{AzureWebJobsStorage}"
  },
  "Host": {
    "LocalHttpPort": 7071,
    "CORS": "*"
  }
}
Enter fullscreen mode Exit fullscreen mode

Tenga en cuenta en el código anterior que ya estamos habilitando CORS. ¡Porque sin él, no podemos realizar operaciones CRUD en el Front-End! Si quieres entender un poco más sobre CORS te recomiendo leer AQUI.

Bueno, la primera parte está lista! ¡Ahora creemos nuestro CRUD en Azure Functions!

Creando la función 'CreateFuncionario'

Para crear una nueva función, simplemente escriba el siguiente comando:

func new
Enter fullscreen mode Exit fullscreen mode

Ingresar este comando le dará varias opciones de plantilla que Azure Functions pone a nuestra disposición. En nuestro caso, como ya se mencionó anteriormente, elija la plantilla: HttpTrigger. Siga los pasos del gif:

gif-serverless-09.gif

Tenga en cuenta que se crearon una carpeta CreateFuncionario y dos archivos:

  • function.json: Aquí definiremos las rutas y los métodos de nuestro endpoint.

  • index.json: Aquí desarrollaremos la lógica del endpoint.

Comencemos a alterar estos archivos. Comenzando con function.json

  • archivo: CreateFuncionario/function.json
{
  "bindings": [{
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": ["post"],
          "route": "funcionarios"
      },
      {
          "type": "http",
          "direction": "out",
          "name": "res"
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Ahora cambiemos el archivo index.js:

  • archivo: CreateFuncionario/index.js
/**
 * Arquivo: CreateFuncionario/index.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável por criar um novo 'Funcionário'
 * Author: Glaucia Lemos
 */

const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const funcionario = req.body || {}

  if (funcionario) {
    context.res = {
      status: 400,
      body: 'Os dados do(a) Funcionário(a) é obrigatório!'
    }
  }

  const { db, connection } = await createMongoClient()

  const Funcionarios = db.collection('funcionarios')

  try {
    const funcionarios = await Funcionarios.insert(funcionario)
    connection.close()

    context.res = {
      status: 201,
      body: funcionarios.ops[0]
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Error ao criar um novo Funcionário(a)'
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Aquí estamos definiendo prácticamente la ruta POST y desarrollando la lógica de Crear un nuevo Empleado.

Vamos a ejecutar este endpoint? Para ejecutar, simplemente escriba el siguiente comando:

> func host start
Enter fullscreen mode Exit fullscreen mode

¡Y enumerará nuestro endpoint creado! Mira el gif:

gif-serverless-11.gif

Es el siguiente endpoint: [POST] http://localhost:7071/api/funcionario

La puerta 7071 es la puerta predeterminado para Azure Functions. ¡Y eso es exactamente lo que necesitaremos poner en nuestro Front End!

Bueno, ahora tomemos esta ruta y agréguela al Front End. Para esto necesitamos hacer algunos cambios al proyecto Front. Vaya a la carpeta front en:front -> src -> app -> funcionario.service.ts y cambie el siguiente archivo funcionario.service.ts

  • archivo: funcionario.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class FuncionarioService {

  // ==> Uri da api (Back-End)
  uri = 'http://localhost:7071/api';

  constructor(private http: HttpClient) { }


(...)
Enter fullscreen mode Exit fullscreen mode

Solo necesitamos cambiar el 'uri' definido en el service de Angular.

En este punto, necesitaremos ejecutar Mongo Compass y Front End. ¡Observe en el gif cómo persistirá el nuevo empleado y ya no necesitaremos la carpeta api del proyecto!

(Haga clic en la imagen para mirar el gif)

gif-serverless-122d9fa9f9e2c32e04.md.gif

¡Persistió maravillosamente! 😍

¡Ahora, hagamos la lista de empleados!

Creando la función 'GetFuncionarios'

Es lo mismo que hicimos anteriormente, creemos una nueva función con el comando: func new, nombremos la funciónGetFuncionarios y cambiemos los archivos: function.json eindex.js

(Haga clic en la imagen para mirar el gif)

gif-serverless-1390be8d86cc5d4218.md.gif

  • GetFuncionarios/function.json
{
    "bindings": [{
            "authLevel": "anonymous",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": ["get"],
            "route": "funcionarios"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}

Enter fullscreen mode Exit fullscreen mode
  • GetFuncionarios/index.js
/**
 * Arquivo: GetFuncionarios/index.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável por listar todos os 'Funcionários'
 * Author: Glaucia Lemos
 */

const createMongoClient = require('../shared/mongo')

module.exports = async context => {
  const { db, connection } = await createMongoClient()


  const Funcionarios = db.collection('funcionarios')
  const res = await Funcionarios.find({})
  const body = await res.toArray()

  connection.close()

  context.res = {
    status: 200,
    body
  }
}
Enter fullscreen mode Exit fullscreen mode

¡Intentémoslo de nuevo! ¡Mira el gif de nuevo!

gif-serverless-14.gif

De nuevo está funcionando perfectamente. Ya notó que es fácil crear un CRUD con Azure Functions, ¿no? ¡Ahora siga los mismos pasos para crear las prójimas funciones!

Creando la función 'GetFuncionarioById'

Ahora que está muy claro para todos aquí lo fácil que es crear un CRUD con Azure Functions, comenzaré a acelerar el proceso de creación y solo informaré lo que ha cambiado en los archivos function.json eindex.js

  • GetFuncionarioById/index.js
{
    "bindings": [{
            "authLevel": "anonymous",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": ["get"],
            "route": "funcionarios/{id}"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  • GetFuncionarioById/function.json
// @ts-nocheck
/**
 * Arquivo: GetFuncionarioById/index.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável por listar Funcionário pelo Id
 * Author: Glaucia Lemos
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params

  if (!id) {
    context.res = {
      status: 400,
      body: 'Por favor, passe o número correto do Id do Funcionário!'
    }

    return
  }

  const { db, connection } = await createMongoClient()

  const Funcionarios = db.collection('funcionarios')

  try {
    const body = await Funcionarios.findOne({ _id: ObjectID(id) })

    connection.close()
    context.res = {
      status: 200,
      body
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Erro ao listar o Funcionário pelo Id.'
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

No probemos ahora. Desarrollemos las dos últimas funciones: Update yDelete.

Creando la función 'UpdateFuncionario'

Una vez más, vamos a crear una nueva función y cambiar los archivos y index.js function.json:

  • UpdateFuncionario/index.js
{
    "bindings": [{
            "authLevel": "anonymous",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": ["put"],
            "route": "funcionarios/{id}"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  • UpdateFuncionario/index.js
// @ts-nocheck
/**
 * Arquivo: UpdateFuncionario/index.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável por atualizar 'Funcionário' por Id
 * Author: Glaucia Lemos
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params
  const funcionario = req.body || {}

  if (!id || !funcionario) {
    context.res = {
      status: 400,
      body: 'Os campos são obrigatórios'
    }

    return
  }

  const { db, connection } = await createMongoClient()
  const Funcionarios = db.collection('funcionarios')

  try {
    const funcionarios = await Funcionarios.findOneAndUpdate(
      { _id: ObjectID(id) },
      { set: funcionario }
    )

    connection.close()

    context.res = {
      status: 200,
      body: funcionarios
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Erro ao atualizar o Funcionário'
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Bueno. Ahora desarrollemos nuestra última función: Delete.

Creando la función 'DeleteFuncionario'

Nuevamente, solo cree una nueva función, elija la opción: HttpTrigger, nombre la funciónDeleteFuncionario y cambie los archivosfunction.json eindex.js:

  • DeleteFuncionario/function.json
{
  "bindings": [{
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": ["delete"],
          "route": "funcionarios/{id}"
      },
      {
          "type": "http",
          "direction": "out",
          "name": "res"
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode
  • DeleteFuncionario/index.js
// @ts-nocheck
/**
 * Arquivo: DeleteFuncionario/index.js
 * Data: 10/11/2019
 * Descrição: arquivo responsável excluir um 'Funcionário' pelo Id
 * Author: Glaucia Lemos
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params

  if (!id) {
    context.res = {
      status: 400,
      body: 'Os campos são obrigatórios!'
    }

    return
  }

  const { db, connection } = await createMongoClient()

  const Funcionarios = db.collection('funcionarios')

  try {
    await Funcionarios.findOneAndDelete({ _id: ObjectID(id) })
    connection.close()
    context.res = {
      status: 204,
      body: 'Funcionário excluído com sucesso!'
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Erro ao excluir Funcionário' + id
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

¡Y nuestro CRUD está listo! Vamos a probar todos los endpoints? ¡Mira el gif!

(Haga clic en la imagen para mirar el gif)

gif-serverless-156824eb58eaca624e.md.gif

Lo más hermoso no es así? Tenga en cuenta nuevamente, que la carpeta api donde hay numerosos archivos, ¡ya no la necesitará!

Todo el código desarrollado está aquí:

👉 Projeto Front-End

👉 Projeto Api - Serverless

Conclusión

Hoy hemos aprendido cómo migrar una aplicación MEAN a Azure Functions, pero persistiendo estos datos localmente y realizando estas funciones localmente. ¿Qué sucede si necesitamos hacer deploy de esta aplicación en la nube? ¿Y cómo sería nuestro backend?

En el próximo post, le explicaré cómo migrar de MongoDb a CosmosDb y cómo implementar estas funciones con una extensión de Azure Tools en Visual Studio Code.

Si desea conocer más detalles sobre Azure Functions, le recomiendo los siguientes cursos totalmente gratuitos de Serverless & Azure Functions y algunos otros recursos importantes:

Cursos Grátis - Azure Functions

Azure para desarrolladores de JavaScript y Node.js

Documentación de Azure Functions

Creando su primera función mediante Visual Studio Code

VS Code Extension – Azure Functions

E-Book Grátis - Azure Serverless Computing Cookbook

Y para saber esta y otras novedades, ¡sígueme en twitter!

Twitter

Hasta pronto! ❤️ ❤️ ❤️

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .