Skip to content
Time de desenvolvedores de software aplicando rest com express no severless framework

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 

  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.

Compartilhe:

Comments (0)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Back To Top