A área de tecnologia se expande cada vez mais, tornando-se um mercado bastante aquecido para…
Aplicando Rest com Express no Serverless Framework
Com a evolução da Cloud e a dissipação dos Micro-services, surgiu então a onda do Serverless, onde não existe a necessidade de gerenciar servidor, pagando apenas pelo tempo que o código fica em execução.
Diversos provedores passaram a disponibilizar este recurso, como Amazon, Azure, IBM, Google Cloud, além de possuir o Kubeless que é um framework nativo para Kubernetes.
Para facilitar a implantação das funções, e permitir ter sua infraestrutura como código, vem o Serverless Framework.
Neste artigo eu utilizo o Serverless Framework para publicar um endpoint NodeJS com Express que acessa o banco de dados DynamoDB.
Aconselho que leia minha última publicação sobre como criar um DNS gratuito, caso queira publicar o endpoint em um domínio próprio com SSL.
Pré-requisitos
- Ter uma conta AWS
- Possuir o NodeJS LTS ou superior
- Possuir o aws-cli instalado
- Instalar o serverless com o comando: $ npm i serverless -g
- Configurar as credenciais aws com o comando:
$ sls config credentials –provider aws –key <aws_key> –secret <aws_passwd>
Começando
Vamos iniciar o projeto com o comando:
$ serverless create –template aws-nodejs –path sls-express
$ cd sls-express
$ npm init
Notem que a estrutura criada, trouxe um novo arquivo YML que contém toda configuração do projeto. Em breve falarei mais sobre ele.
Agora vamos adicionar as dependências de runtime
- aws-sdk: SDK para acessar os recursos da amazona
- serverless-http: Permite fazer wrap para frameworks JS, como o Express
- express: Framework web
- body-parser: Middleware para faze parse do conteúdo body
Para instalar utilizamos o comando:
npm i aws-sdk body-parser express serverless-http
Também instalaremos duas dependências de desenvolvimento:
- serverless-dynamodb-local: Simula o banco dynamodb em sua máquina
- serverless-offline: Plugin para subir o ambiente em sua máquina
Para instalar utilizamos o comando:
npm i -D serverless-dynamodb-local serverless-offline
Certo, temos todas dependências necessárias para testar localmente e subir em produção.
Iniciando a Codificação
Vamos começar a codificar, então abra o arquivo handle.js e apague todo o conteúdo.
Começaremos importando as bibliotecas necessárias para este exemplo:
‘use strict’
const serverless = require(‘serverless-http’);
const express = require(‘express’)
const bodyParser = require(‘body-parser’);
const app = express()
const AWS = require(‘aws-sdk’);
Agora vamos adicionar a configuração do banco de dados, onde a variável IS_OFFLINE é definida pelo plugin serverless-offline. Neste caso quando estiver rodando offline, os dados do dynamodb serão apontados para localhost.
const TABLE = ‘todo’;
const dynamoDb;
if (process.env.IS_OFFLINE) {
dynamoDb = new AWS.DynamoDB.DocumentClient({
region: ‘localhost’,
endpoint: ‘http://localhost:8000’,
accessKeyId: ‘DEFAULT_ACCESS_KEY’,
secretAccessKey: ‘DEFAULT_SECRET’
});
} else {
dynamoDb = new AWS.DynamoDB.DocumentClient();
}
Para configurar o body-parser no express adicione a linha
app.use(bodyParser.json({ strict: false }));
Criaremos 2 métodos, um GET e um POST registrados no express:
app.get(‘/todo/:id’, (req, res) => {
const params = {
TableName: TABLE,
Key: {
_id: req.params.id,
},
}
dynamoDb.get(params, (error, result) => {
if (error) {
console.log(error);
res.status(400)
.json({ error: ‘Não foi possível recuperar a tarefa’ });
}
if (result.Item) {
const {_id, title} = result.Item;
res.json({ _id, title });
} else {
res.status(404)
.json({ error: “Tarefa não encontrada” });
}
});
});
app.post(‘/todo’, (req, res) => {
const { _id, title } = req.body;
if (typeof _id !== ‘string’) {
res.status(400).json({ error: ‘”_id” deve ser uma string’ });
} else if (typeof title !== ‘string’) {
res.status(400).json({ error: ‘”title” deve ser uma string’ });
}
const params = {
TableName: TABLE,
Item: { _id, title },
};
dynamoDb.put(params, (error) => {
if (error) {
console.log(error);
res.status(400).json({ error: ‘Não foi possível criar a tarefa’ });
}
res.json({ _id, title });
});
})
Para registrar o express no contexto do lambda, adicione ao final:
module.exports.handler = serverless(app);
Pronto, toda codificação esta pronta, agora o próximo passo é codificar a infraestrutura.
Criando a infraestrutura
Lembrando que o conceito de Serverless é que nos preocupemos com o código, e não com a configuração de servidor. Você deve estar se perguntando, “por que tenho que codificar minha infraestrutura?”.
Certo, não iremos codificar o servidor, mas os recursos AWS que complementam nossa função, como por exemplo a tabela do DynamoDB, DNS, API Gateway, Role de acesso, etc.
O Serverless Framework utiliza um arquivo chamado serverless.yml para configurar a infraestrutura. Ele tem algumas abstrações para gerar a estrutura, porém aceita códigos do CloudFormation também quando utilizado o provider AWS, pois no final, tudo vira um arquivo do CloudFormation.
Provavelmente o arquivo em sua máquina esteja agora todo comentado com alguns exemplos. Caso queira estudá-lo por completo, olhe a documentação oficial. Neste exemplo mostrarei o YML completo, então pode apagar todo o conteúdo para fazer passo a passo.
Primeiro vamos adicionar o nome da função lambda, o nome da aplicação e os plugins que utilizaremos para rodar offline.
service: todo-list-function
app: todo-list
plugins:
– serverless-dynamodb-local
– serverless-offline
Agora vamos adicionar as configurações do plugin serverless-dynamodb-local para subir o dynamodb localmente.
custom:
dynamodb:
stages:
– local
start:
port: 8000
inMemory: true
heapInitial: 200m
heapMax: 1g
migrate: true
convertEmptyValues: true
Agora vamos adicionar as configurações do provider, qual o container rodar, a região, o nome do script no cloudFormation, quantidade de memória disponível, tempo máximo para execução, tempo de retenção de log e o nome do serviço para o API Gateway.
provider:
name: aws
runtime: nodejs10.x
region: us-east-1
stackName: todo-list-stack
memorySize: 128
timeout: 2
logRetentionInDays: 3
apiName: ServerlessRestApi
Dentro de provider ainda, vamos fazer a criação da role com acesso ao DynamoDB.
iamRoleStatements:
– Effect: Allow
Action:
– dynamodb:Query
– dynamodb:Scan
– dynamodb:GetItem
– dynamodb:PutItem
– dynamodb:UpdateItem
– dynamodb:DeleteItem
Resource:
– { “Fn::GetAtt”: [“TodoDynamoDBTable”, “Arn” ] }
Agora vamos definir as rotas disponíveis, e quais funções serão chamados para cada. Como estamos utilizando o express, utilizarei o método “ANY” para que todas chamadas mandem direto para dentro do handle do express. Para mais detalhes leia esta documentação.
functions:
app:
handler: handler.express
events:
– http: ANY /
– http: ‘ANY {proxy+}’
Para finalizar, vamos codificar a criação da tabela do DynamoDB:
resources:
Resources:
TodoDynamoDBTable:
Type: ‘AWS::DynamoDB::Table’
Properties:
AttributeDefinitions:
–
AttributeName: _id
AttributeType: S
KeySchema:
–
AttributeName: _id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: todo
Pronto, o código está pronto para ser executado.
Para executar localmente utilize o comando:
$ sls offline start –stage local
Para fazer o deploy na AWS utilize o comando:
$ sls deploy
Para remover toda stack utilize:
$ sls remove
Existem diversos comandos para invocar a função, ou buscar logs, porém vou disponibilizar apenas este link com a documentação, pois o intuito do artigo é a criação do endpoint. Quem sabe em um próximo eu não traga novidade sobre monitoramento de lambdas.
Bônus
Achou que eu faria você ler meu artigo anterior e não mostraria a criação do endpoint com um DNS próprio?
Então vamos lá, no arquivo serverless.yml no atributo resources vamos adicionar a configuração do Nome de domínio do API Gateway. Substitua o arn do certificado para o seu já criado e também o nome do domínio.
ApiGatewayCustomName:
Type: AWS::ApiGateway::DomainName
Properties:
CertificateArn: arn:aws:acm:arn-seu-certificado
DomainName: seudominio.ml
Abaixo agora vamos mapear a rota do nome do domínio para o stage, neste caso, é possível configurar sub-rotas para ambientes diferentes. Mas o exemplo vai direcionar tudo “/” para a nossa aplicação.
ApiMapping:
Type: ‘AWS::ApiGateway::BasePathMapping’
DependsOn: [ApiGatewayRestApi]
Properties:
BasePath: ”
DomainName: !Ref ApiGatewayCustomName
RestApiId: !Ref ApiGatewayRestApi
Stage: dev
E para finalizar vamos configurar o registro do Route53 para apontar para o API Gateway. Lembre-se de alterar o campo “seudominio.ml” para o DNS configurado na AWS.
Route53Api:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: seudominio.ml.
Comment: Alias para API Gateway.
RecordSets:
– Name: app.seudominio.ml.
Type: A
AliasTarget:
EvaluateTargetHealth: false
HostedZoneId: !GetAtt ApiGatewayCustomName.DistributionHostedZoneId
DNSName: !GetAtt ApiGatewayCustomName.DistributionDomainName
Faça o deploy conforme ensinado na seção anterior (pode levar até uns 30 minutos por conta da criação do DomainName).
Agora você já pode acessar seu endpoint em https://seudominio.ml/todo/teste
Este projeto encontra disponível no Github.
E aí, curtiu a facilidade de disponibilizar domínios Rest com HTTPS?
Espero que tenha o ajudado e até a próxima.
Sobre o autor
Ramon Nunes é formado em Sistemas de Informação pela UNISUL, atualmente está finalizando a especialização em Arquitetura de Software. Integrante da equipe IT Services da DB1 Global Software, atuando como desenvolvedor Java.
Se interessa por tecnologias e técnicas que impactam na arquitetura de sistemas e na quebra de paradigmas.
Comments (0)