Migrando uma Aplicação MEAN para Arquitetura Serverless & Azure Functions
Glaucia Lemos
Posted on September 12, 2019
Este artigo faz parte da coleção de conteúdos do #ServerlessSeptember. Aqui você encontrará todos os artigos publicados durante o mês de Setembro de 2019.
Durante esse artigo você aprenderá a realizar a migração de uma maneira simples, rápida e dinâmica de uma aplicação MEAN para uma arquitetura Serverless, fazendo uso do Azure Functions!
O projeto MEAN já está pronto e vocês podem fazer um git clone ou download AQUI
E, caso desejam ver a palestra sobre o assunto mencionado bastam ver o vídeo abaixo da palestra dada na BrazilJs de 2019, onde eu falo justamente sobre Azure Functions + Node.js + Arquitetura Serverless (agradeço imensamente a BrazilJs pelo convite e a disponibilização do vídeo):
YouTube:
Vamos nessa?!
Entendendo a estrutura do Projeto MEAN
Nesse projeto vamos focar nas duas pastas: api e front. Conforme a imagem abaixo:
Se vocês executarem essa aplicação observarão que estamos persistindo essa aplicação no MongoDb e usando o Back-End, que nesse caso estamos usando o Node.js
Os dados persistidos consiste em:
Classe: Funcionario
- idFuncionario: (number - guid gerado pelo MongoDb)
- nomeFuncionario: string
- cargo: string
- numeroIdentificador: number
Caso desejam executar localmente esse projeto, bastam seguir os passos no README.md do repositório do projeto.
Bom, agora que vocês estão com o projeto MEAN em mãos, vamos começar a fazer a migração para o Azure Functions?!
Mas antes, vamos entender o que seria o Azure Functions!
O que é Azure Functions?! ⚡️
Azure Functions é um serviço de computação serverless que permite executar o facilmente pequenos trechos de código ou funções na nuvem sobre demanda sem precisar provisionar ou gerenciar a infraestrutura.
E o Azure Functions possui suporte a inúmeras linguagens, entre elas:
Já as linguagens abaixo, já possui suporte, porém estão na sua versão preview:
- Bash
- PHP
Se desejarem saber mais detalhes das linguagens que possuem suporte ao Azure Functions, bastam acessar o link AQUI.
Porém, para esse artigo focaremos no JavaScript! 😉
Templates Importantes no Azure Functions
Antes de começar a realizar a migração é importante mencionar que o Azure Functions dispõe de inúmeros templates já prontos e preparados só para começar a serem usados. Entre eles:
- HTTPTrigger
- TimerTrigger
- CosmosDBTrigger
- BlobTrigger
- QueueTrigger
- EventGridTrigger
- EventHubTrigger
- ServiceBusQueueTrigger
- ServiceBusTopicTrigger
Não entrarei em detalhes de cada um, pois senão esse artigo ficará muito grande. Mas, caso desejam entender mais sobre cada template e qual o seu melhor uso numa determinada aplicação, recomendo a leitura na documentação AQUI.
Para esse post, estaremos fazendo uso do template: HTTPTrigger uma vez que esse template dispara a execução do seu código usando uma solicitação HTTP. E é justamente o que precisaremos para realizar a migração!
Caso você seja um(a) estudante de alguma Instituição de Ensino de Faculdade ou Universidade, poderá criar sua conta no 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. Com essa conta, você poderá fazer uso de 1.000.000 solicitações gratuitas por mês para processar eventos no Azure Functions!
Bom, depois desse overview sobre Azure Functions, podemos enfim começar a nossa migração! Vamos que vamos!
Instalando o pacote Azure Functions Core Tools
O Azure Functions Core Tools nos permitirá desenvolver e testar as funções de maneira local na nossa máquina a partir de um terminal ou prompt de comando.
Abaixo seguem os programas e o pacote que precisaremos para continuar no nosso tutorial:
Depois que você tiver o Node.js instalado na sua máquina, basta digitar o seguinte comando:
- Windows
npm install -g azure-functions-core-tools
- MacOs
brew tap azure/functions
brew install azure-functions-core-tools
- 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
Para maiores informações para instalar de maneira correta o Azure Functions Core Tools, bastam acessar o link AQUI
E como vou saber se de fato o meu Azure Functions Core Tools está instalado de maneira correta na minha máquina?! Bastam digitar o seguinte comando no terminal:
> func
Se acontecer conforme o gif abaixo é porque o pacote foi instalado com sucesso!
Ótimo. Agora, podemos criar as nossas funções. Para isso, crie um pasta local na sua máquina e vamos começar!
Criando uma Nova Aplicação no Azure Functions
Agora que já temos instalado o pacote, vamos criar uma nova aplicação. Para isso, bastam seguir o passos conforme o gif abaixo:
Observam que, quando abrimos o Visual Studio Code, precisamos clicar no botão YES
que aparece no canto inferior direito para habilitar alguns arquivos importantes no projeto.
Criando a Conexão com o MongoDb
Bom, vamos fazer agora algumas alterações necessárias no nosso projeto recém criado. Para isso, vamos instalar localmente o mongodb no nosso projeto. Digitem o seguinte comando:
> npm install mongodb
Ao instalar o mongoDb no projeto, observem que houve alterações no arquivo package.json
. No final o arquivo deverá ficar da seguinte maneira:
- arquivo: 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"
}
}
Agora, vamos criar uma pasta chamada: shared
e dentro dela vamos criar o arquivo: mongo.js
. A estrutura do projeto agora ficará da seguinte maneira:
Vamos agora alterar o arquivo mongo.js
. Para isso, inclua o bloco de código abaixo:
- arquivo: 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;
Aqui estamos criando a nossa conexão local com o MongoDb! Muito parecido com o que já fazemos no Back-End com o Node.js, não é mesmo?!
E vamos também alterar o arquivo local.settings.json
. Esse arquivo é responsável por 'guardar' todas as keys que não queremos que estejam expostas na hora de realizar o commit. Notem que esse arquivo está na lista de arquivos no .gitignore
.
Abram o arquivo local.settings.json
e façam as seguintes alterações:
- arquivo: local.settings.json
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsStorage": "{AzureWebJobsStorage}"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "*"
}
}
Notem no bloco de código acima que já estamos habilitando o CORS
. Pois sem ele, não conseguimos realizar as operações de CRUD no front! Se desejarem entender um pouco mais sobre o CORS recomendo a leitura AQUI.
Bom, a primeira parte já está pronta! Agora vamos criar o nosso CRUD no Azure Functions!
Criando a função 'CreateFuncionario'
Para criar uma nova função bastam digitar o seguinte comando:
func new
Ao digitar esse comando ele dará várias opções de templates que o Azure Functions nos disponibiliza. No nosso caso, conforme já mencionado acima, vamos escolher o template: HttpTrigger
. Sigam os passos do gif abaixo:
Observem que, foi criado uma pasta CreateFuncionario
e dois arquivos:
function.json: aqui iremos definir as rotas e os métodos do nosso endpoint.
index.json: aqui iremos desenvolver a lógica inerente ao endpoint.
Vamos começar a alterar esses arquivos. Começando pelo function.json
- arquivo: CreateFuncionario/function.json
{
"bindings": [{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["post"],
"route": "funcionarios"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
Agora vamos alterar o arquivo index.js
:
- arquivo: 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)'
}
}
}
Aqui estamos praticamente definindo a rota do Post
e desenvolvendo a lógica do Criar um novo Funcionário
.
Vamos executar esse endpoint?! Para executar, bastam digitar o seguinte comando:
> func host start
E ele irá listar o nosso endpoint criado! Vejam no gif:
Ele lista para nós o seguinte endpoint: [POST] http://localhost:7071/api/funcionario
A porta 7071
é a porta default do Azure Functions. E é justamente ela que iremos precisar para colocar no nosso Front-End!
Bom, vamos agora pegar essa rota e adicionar no Front-End! Para isso, precisamos realizar algumas alterações no projeto Front
. Vão até a pasta front
em: front -> src -> app -> funcionario.service.ts
e alterem o seguinte arquivo funcionario.service.ts
- arquivo: 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) { }
(...)
Só precisamos alterar a uri
definida no service do Angular.
Nesse momento, precisaremos executar o Mongo Compass e o Front-End. Observem no gif como persistirá o novo funcionário e que não precisaremos mais da pasta api
do projeto!
(Clique na imagem abaixo para visualizar o gif)
Persistiu lindamente! 😍
Agora, vamos fazer o listar!
Criando a função 'GetFuncionarios'
É a mesma premissa que foi feita acima, vamos criar uma nova função com o comando: func new
, nomear a função de GetFuncionarios
e alterar os arquivos: function.json
e index.js
(Clique na imagem abaixo para visualizar o gif)
- GetFuncionarios/function.json
{
"bindings": [{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get"],
"route": "funcionarios"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
- 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
}
}
Vamos testar novamente! Vejam novamente o gif abaixo!
Novamente está funcionando perfeitamente. Já observaram que é fácil criar um CRUD com o Azure Functions, não é mesmo?! Agora é só seguir os mesmos passos para criar as próximas funções!
Criando a função 'GetFuncionarioById'
Agora que já ficou muito claro a todos aqui como é fácil criar um CRUD com o Azure Functions, vou começar a acelerar o processo de criação e só informar o que foi alterado nos arquivos function.json
e index.js
- GetFuncionarioById/index.js
{
"bindings": [{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get"],
"route": "funcionarios/{id}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
- 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.'
}
}
}
Não vamos testar agora. Vamos desenvolver as duas últimas funções: Update
e Delete
.
Criando a função: 'UpdateFuncionario'
Novamente, vamos criar uma nova função e alterar os arquivos function.json
e index.js
:
- UpdateFuncionario/index.js
{
"bindings": [{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["put"],
"route": "funcionarios/{id}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
- 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'
}
}
}
Show! Agora vamos desenvolver a nossa última função: Delete
!
Criando a função: 'DeleteFuncionario'
Novamente, bastam criar uma nova função, escolher a opção: HttpTrigger
, nomear a função de DeleteFuncionario
e alterar os arquivos function.json
e index.js
:
- DeleteFuncionario/function.json
{
"bindings": [{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["delete"],
"route": "funcionarios/{id}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
- 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
}
}
}
E está pronto o nosso CRUD! Vamos testar todos os endpoints?! Vejam o gif abaixo!
(Clique na imagem abaixo para visualizar o gif)
Coisa mais linda, não é mesmo?! Notem mais uma vez que, aquela pasta api
onde há inúmeros arquivos, não terá mais necessidade! Podemos praticamente deletar aquela pasta inteira!!!
Todo o código fonte desenvolvido estão aqui:
Palavras Finais
Hoje aprendemos a realizar a migração de uma aplicação MEAN para o Azure Functions, porém persistindo esses dados localmente e executando essas funções localmente. E se porventura precisarmos hospedar essa aplicação na Nuvem? E o nosso Back-end como ficaria?
No próximo post, estarei explicando a vocês como realizar a migração do MongoDb para o CosmosDb e como realizar o deploy dessas funções usando uma extensão do Azure Tools no próprio Visual Studio Code.
Se vocês desejam saber mais detalhes sobre Azure Functions recomendo a todos vocês os seguintes cursos totalmente gratuitos de Serverless & Azure Functions e alguns outros recursos importantes:
✅ Cursos Grátis - Azure Functions
✅ Azure para devs JavaScript & Node.js
✅ Documentação Azure Functions
✅ Criando a sua Primeira Função no Visual Studio Code
✅ Extensão Vs Code – Azure Functions
✅ E-Book Grátis - Azure Serverless Computing Cookbook
E, para ficarem por dentro das últimas atualizações não deixem de me seguir no Twitter! 😃
Até a próxima pessoal! ❤️ ❤️ ❤️
Posted on September 12, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 12, 2019
September 12, 2019