Time de desenvolvedores de software aplicando rest com express no severless framework
Desenvolvimento

Aplicando Rest com Express no Serverless Framework

Time de desenvolvedores de software aplicando rest com express no severless framework
Ramon Teixeira
29 de agosto de 2019
Time de desenvolvedores de software aplicando rest com express no severless 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 

  1. Ter uma conta AWS 
  2. Possuir o NodeJS LTS ou superior 
  3. Possuir o aws-cli instalado 
  4. Instalar o serverless com o comando: $ npm i serverless -g 
  5. 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.


Escrito por

Ramon Teixeira
Ramon Teixeira é formado em Sistemas de Informação pela UNISUL, atualmente esta 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.

Inscreva-se e receba nossa newsletter!

Estamos sempre gerando conteúdos inéditos para compartilhar conhecimento com você, além das últimas notícias de tecnologia.