Autenticação em Node.js com Passport – Parte 2

Atualizado em 26/08/2018!

Este post é uma continuação do tutorial de como implantar um mecanismo de autenticação completo em Node.js usando o middleware Passport. No tutorial passado fizemos funcionar a autenticação através de uma tela de login e impedimos que usuários anônimos entrem na tela de chat.

Nesta segunda parte vamos criar a tela de cadastro de usuário (Sign Up) e a tela de recuperação de senha (Forget Password), além que aprenderemos a enviar emails em Node.js.

  1. Criando o cadastro de usuário
  2. Aprendendo a enviar emails
  3. Criando a recuperação de senha

Se quiser ver este mesmo tutorial em formato de video aula, confira o meu curso online de Node.js e MongoDB. Caso prefira apenas o texto mesmo, vamos lá!

#1 – Criando o cadastro de usuário

No tutorial anterior deixamos nosso banco preparado e a conexão compartilhada em uma variável global.db. Usaremos esta conexão global para fazer as operações de banco necessárias. Também usaremos o mesmo módulo bcrypt para criptografar as senhas no banco.

Para começar, vamos criar a nossa tela de cadastro de usuário que pedirá username, password e email, chamei ela de views/signup.ejs:

Note que este form faz um POST para uma rota /users/signup e que ele já espera uma message no model para exibir erro, caso dê algum problema. Faremos isso tudo funcionar depois.

Para que essa view possa ser exibida, primeiro vamos criar um novo arquivo de rotas em routes/users.js. Aqui colocaremos todas as rotas relacionadas a usuários, a começar pela rota que exibe a view que acabamos de criar via GET:

Note que já fiz o teste para passar ou não uma mensagem de falha para a tela no model, pra não dar erro na minha view. Agora para que essa rota passe a funcionar, temos de configurar nosso app.js, como abaixo:

Salve tudo e mande rodar sua aplicação para ver ela funcionando no navegador (use o link de “Não possui cadastro?” que deixamos prontos na tela de login):

Cadastro de Usuário
Cadastro de Usuário

Note que para acessar essa tela a rota é /users/signup. Isso porque definimos no app.js que rotas ‘/’ são tratadas pela index.js e rotas ‘/users’ são tratadas pela users.js, ou seja, há um roteamento inicial com ‘/users’ e depois o outro roteamento com ‘/signup’, virando ‘/users/signup’ a rota completa.

Agora, antes de sair programando a rota POST /users/signup que está em nosso HTML FORM, devemos criar a função JS que vai salvar um novo usuário no MongoDB. Para armazenar não apenas essa mas outras funções relacionadas a banco de dados (que não são de autenticação, pois estas deixamos no arquivo auth.js), vamos criar um arquivo db.js na raiz do nosso projeto. Dentro dele, vamos criar e expor uma função createUser, como abaixo:

Aqui eu pego a senha plain-text enviada pela view e gero o hash dela usando um salt de 10 rounds, que foi o que achei como sendo seguro atualmente, embora esta resposta dê um panorama mais completo de como calcular isso se seu sistema realmente precisar de uma segurança acima da média (bancos?). Ao final do processo, chamo uma função de callback que a createUser espera como último parâmetro.

Com esse módulo db.js pronto, programar essa rota de /users/signup será bem simples! Vá em routes/users.js e adicione uma rota POST para /signup, como abaixo (lembrando que o path /users já é tratado no app.js):

Aqui eu carrego o módulo ‘db’ que acabamos de criar e com ele chamo a função createUser, passando os dados enviados no corpo da requisição HTTP: username, password e email. Como último parâmetro, passo uma arrow function como callback, que apenas vai redirecionar para a tela de login em caso de sucesso e para a própria tela de signup em caso de fracasso, onde uma mensagem de erro será exibida.

Execute novamente sua aplicação e teste a criação de usuário. Se tudo deu certo, após a criação você poderá se autenticar na tela de login usando esse novo usuário.

Mas o que você acha de mandarmos um email de boas vindas para esse usuário que recém se cadastrou?

Curso Node.js e MongoDB
Curso Node.js e MongoDB

#2 – Aprendendo a enviar emails

Após o usuário se cadastrar, vamos enviar um email de boas vindas para ele. Além de ser algo interessante, fazer a lógica de envio de emails será útil na próxima etapa do tutorial, que é reset de senha por email.

Para fazer o envio de emails vamos usar um módulo chamado Nodemailer, que deve ser instalado via linha de comando usando o NPM:

Depois, vamos criar um módulo mail.js na raiz do nosso projeto, com o seguinte código:

Nosso módulo mail.js apenas expõe uma função que espera o destinatário da mensagem (to), o assunto (subject) e o texto (text). Por fim, a função sendMail faz o serviço propriamente dito.

Note que as configurações de SMTP para envio de email estão definidas em variáveis de ambiente, assim como fizemos na parte 1 deste tutorial para guardar a connection string do MongoDB.

Não esqueça que para que essas variáveis de ambiente funcionem, você deve adicionar o modelo delas no arquivo .env.example (é um arquivo oculto, você só vai conseguir ver ele com VS Code ou no terminal):

E com os valores finais no arquivo .env:

Aqui usei minha conta de email na Umbler para enviar os emails (você pode criar sua conta lá e ganhar créditos de graça para gastar com um email que custa centavos por mês). Como é uma aplicação de teste que vai enviar poucos emails, não vamos ter problemas. No entanto, caso queira usar o nodemailer para enviar muitos emails, você rapidamente será bloqueado em qualquer provedor de hospedagem. O certo é usar serviços de SMTP Gateway como Amazon SES, Mandrill e SendGrid.

Agora, para usarmos este módulo e finalmente fazer o envio de email de boas vindas funcionar, adicione apenas uma linha a mais em nossa rota POST em users.js:

Obviamente você pode personalizar essa mensagem livremente, ao invés do “email espartano” que deixei configurado. Soluções mais profissionais geralmente envolvem criar templates HTML que são lidos usando o módulo ‘fs’, algumas variáveis dentro do template são trocadas e depois a string resultante é enviada como HTML.

Rode novamente sua aplicação com ‘npm start’ e realize o cadastro de um novo usuário, mas informando um email válido, para que a mensagem chegue e você possa ver o resultado no seu webmail.

Email enviado via Node.js
Email enviado via Node.js

#3 – Criando a recuperação de senha

Agora a próxima parte é a de recuperação de senha. Lembra que deixamos um link pra isso lá na tela de login?

Vamos começar criando uma nova view, a views/forgot.ejs, com um formulário bem simples que pede a confirmação de seu email para envio de uma nova senha:

E para que essa tela seja acessível, vamos criar uma nova rota GET em routes/users.js:

Rodando sua aplicação e acessando no navegador, já deve ser possível navegar até esta página usando o link na tela de login:

Esqueceu a senha
Esqueceu a senha

Para fazer funcionar este formulário, vamos começar criando duas funções em nosso arquivo db.js: uma que busca um usuário pelo seu email e outra que muda a senha de um usuário:

A primeira função é bem simples, um findOne por email, que executa um callback após encontrar (ou não) o usuário. A segunda recebe uma senha, criptografa ela e sobrescreve o hash de senha do usuário cujo id foi passado por parâmetro. O module.exports no final do db.js também foi atualizado de acordo.

Note que não estou criando as devidas validações em nenhum formulário para os artigos não ficarem gigantescos. Nem de campos obrigatórios, nem validações de regras de negócio como não permitir usuários e emails duplicados. Tenha em mente que você deverá implementar essas questões se for usar esses códigos em produção.

Antes de sairmos fazendo a rota POST, vamos criar um arquivo utils.js na raiz do nosso projeto e colocar dentro dele uma função de geração de senha aleatória:

Esta função é bem simples e talvez você até queira usar alguma mais avançada. Aqui eu crio e retorno uma senha aleatória de 10 caracteres alfanuméricos (maiúsculas, minúsculas e números).

Agora que temos estas funções podemos criar a nossa rota POST que vai receber os dados do formulário de “esqueci minha senha”. Abra o arquivo routes/users.js e crie a nova rota:

Aqui começamos com o findUser que criamos anteriormente, que busca usuário por email. Caso não encontre, vamos jogar o usuário para a tela de login mesmo assim, embora você possa pensar em algo mais criativo para fazer.

Caso encontre, mandamos gerar uma nova senha usando a função que criei há pouco e uso o changePassword para mudar a senha do usuário que possui o email especificado (em um sistema de produção, para evitar problemas, além do email, peça alguma informação pessoal do usuário para confirmar esse processo de troca de senha). Por fim, um email é enviado com a senha que acabou de ser gerada, como na imagem abaixo.

Nova senha
Nova senha

Se você tentar se autenticar com a senha antiga, notará que ela não funciona mais, somente a nova.

É uma boa prática guardar o hash das últimas senhas do usuário e não deixar que ele use senhas antigas (mesmo senhas aleatórias podem conflitar ocasionalmente). Outra boa prática seria no primeiro login com a nova senha ele ser solicitado a cadastrar uma senha pessoal ao invés dessa aleatória. Enfim, sempre há margem para melhorar quando o assunto é segurança.

Não é exatamente uma parte 3, mas este tutorial sobre JSON Web Token ajuda a preencher a lacuna de autorização (visto que até o momento vimos apenas autenticação). No caso de estar utilizando arquitetura microservices, é uma boa colocar sua autenticação vinculada a regras de autorização no seu API Gateway.

Até a próxima!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js ou clique neste link para conhecer o meu curso online!

Arquitetura de micro serviços em Node.js + MongoDB: Parte 4

E chegamos à quarta parte da nossa série de artigos sobre como implementar na prática um projeto de sistema usando arquitetura de microservices usando Node.js e MongoDB.

Na primeira parte desta série eu dei uma introdução teórica sobre microservices, além de falar do propósito de usar Node e Mongo, apresentando nosso case que é ‘digno de cinema’. 😉

Na segunda parte, começamos a estruturar nosso projeto e preparamos toda a camada de dados e testes unitários do primeiro micro serviço.

Na terceira parte, finalizamos o desenvolvimento do primeiro micro serviço, o movies-service, que nos forneceu acesso a três chamadas:

  • /movies: traz todos filmes;
  • /movies/premieres: traz todos lançamentos;
  • /movies/{id}: traz um filme por id;

Nesta quarta parte, conforme o trecho abaixo do case, vamos construir o microservice cinema-catalog-service, aproveitando bastante da expertise que obtivemos com o micro serviço anterior. A ideia é que este serviço receba a requisição do front-end e providencie o restante, inclusive chamando o movies-service quando necessário.

Consulta de Lançamentos do Cinema
Consulta de Lançamentos do Cinema

Veremos nesta etapa da série:

  1. Estruturando o Micro Serviço
  2. Modelando o banco de dados
  3. Subindo o banco e conectando
  4. Iniciando o Repositório

Então mãos à obra!

Curso Node.js e MongoDB
Curso Node.js e MongoDB

#1 – Estruturando o Micro Serviço

 

Este microservice chamado cinema-catalog-service deve ser uma pasta dentro do seu projeto cinema-microservice. No seu terminal, navegue até a pasta src com ‘cd’ e depois use o comando ‘npm init’ para gerar o packages.json e adicione o script ‘start’ nele para facilitar nossas execuções depois, deixando-o como abaixo.

Na sequência, crie pastas e arquivos para ficar com a seguinte configuração abaixo:

  • cinema-microservice
    • cinema-catalog-service
      • data
      • src
        • api
          • cinema-catalog.js
          • cinema-catalog.test.js
        • config
          • mongodb.js
          • mongodb.test.js
        • repository
          • repository.js
          • repository.test.js
        • server
          • server.js
          • server.test.js
        • index.js
        • index.test.js
        • packages.json
        • .env
        • .env.example

Lembrando que na pasta data você deve apontar o seu banco de dados quando criarmos ele.

Aproveite este momento de configuração para rodar o comando abaixo no terminal (dentro da pasta src) para instalar as dependências que vamos precisar:

Alguns dos arquivos acima nós vamos conseguir aproveitar do outro micro serviço. Sim, nós copiaremos alguns arquivos na cara dura. Essa é uma atitude bem polêmica dentro da engenharia de software mas perfeitamente natural sob a ótica da arquitetura de micro serviços.

Copie os seguintes arquivos do outro microservice para este:

  • /config/mongodb.js
  • /config/mongodb.test.js
  • /server/server.js
  • /server/server.test.js
  • /.env.example

Mesmo os demais arquivos não sendo copiados na íntegra, aproveitaremos e muito a lógica deles, como você verá mais tarde.

#2 – Modelando o Banco de Dados

Vamos voltar a falar de banco de dados?

Isso porque cada microservice deve ter a sua própria base de dados, para garantir sua independência dos demais. No caso do movies-service, o banco armazena os dados dos filmes. No caso deste cinema-catalog-service, o banco armazenará os dados das salas de cinema da rede.

O domínio deste banco de dados são as seguintes informações:

  • id da cidade;
  • nome da cidade;
  • uf da cidade (sigla string);
  • código do país (duas letras, opcional, caso seja uma rede internacional);
  • cinemas da cidade;
    • id do cinema;
    • nome do cinema (geralmente nome da rede + nome do shopping);
    • salas de cinema;
      • nome da sala (numérico simples);
      • sessões;
        • data e hora (date);
        • id do filme (referente ao banco de filmes);
        • nome do filme;
        • valor (decimal);
        • assentos;
          • número do assento;
          • disponível (booleano);

Note que já deixei a modelagem desse banco meio tendenciosa com a indentação acima, mas vale alguma discussão do porquê desta estrutura. Primeiro, lembre-se que o objetivo deste banco é fornecer os dados das salas de cinema de uma cidade, fornecendo a agenda e disponibilidade das mesmas, para que o app de cinema permita essas consultas.

Segundo, temos uma série de típicas relações aqui:

  • cada país tem N estados (1-N);
  • cada estado tem N cidades (1-N);
  • cada cidade tem N cinemas (1-N);
  • cada cinema tem N salas (1-N);
  • cada sala de cinema tem N sessões (1-N);
  • cada sessão tem N assentos (1-N);
  • cada sessão tem 1 filme (1-1);

Note que em um banco relacional típico, seguindo todas as Formas Normais, teríamos 6 tabelas e 7 relacionamentos. Em MongoDB e mais ainda, em um micro serviço com foco na entrega de informações de salas de cinema, não há a necessidade de quebrarmos em tantas divisões assim. Isso porque a forma como este banco será consultado é bem específica: um usuário irá passar (de forma automática ou manual) a informação da sua cidade para saber as salas de cinema e suas sessões.

Dentro desse contexto, nossa chave com certeza é a informação da cidade e, a partir daí, desenrolamos o restante das informações na proporção de 1-N. Se a informação de cidade por si só não agrega informação ao sistema, ela tranquilamente pode estar atrelada diretamente às demais informações. Caso ela tivesse relevância, seria não apenas o caso de ser outra coleção mas sim outro microservice, focado em locais, certo?

Sendo assim, temos o seguinte schema (que facilmente pode ser modificado depois, afinal estamos falando de MongoDB):

Algumas informações são nomes, como o nome dos filmes, que vamos “repetir” aqui em relação à informação original que fica no outro micro serviço e consequentemente no outro banco de dados. Em bancos relacionais, garantimos a integridade desta informação através da segregação da mesma em uma tabela separada e chaves estrangeiras, no entanto, isso deixa a informação mais custosa de ser obtida de volta (os famosos JOINs).

Assim, em uma abordagem não-relacional deixamos a responsabilidade de garantir a integridade dos nomes para a aplicação. No caso dos nomes dos filmes disponíveis nas sessões, replicamos esta informação para que em listagens simples não seja necessário pedir ao outro microservice, assim, somente temos de ir no movies-service em caso de pegar detalhes de um filme, caso em que usaremos o id do mesmo (que deve ser igual entre todas bases que fizerem referências a filmes). Essa responsabilidade de integridade do nome dos filmes inclusive deve ser uma preocupação nos casos em que o nome tenha de ser atualizado por algum motivo, pois assim ele deverá ser atualizado em mais de um banco, o que gerará mais transtorno do que o normal, mas não é impossível (é o preço que se paga por facilidade nas consultas).

Relações 1-N de entidades simples, como assentos, podem facilmente ser substituídas por campos multivalorados (arrays) e mesmo entidades mais complexas, mas que não fazem sentido existirem de maneira independente, são arrays de subdocumentos.

Uma regra boa de decidir se um documento deve existir de maneira independente como outra coleção ou se deve ser um subdocumento de outro já existente é se lembrar dos conceitos de Agregação e Composição da Orientação à Objetos. Se o objeto seria uma agregação, ou seja, existe de mesmo que o objeto “pai” seja destruído, ele deve ser de outra coleção no MongoDB, caso contrário, faça-o ser um subdocumento do documento “pai”.

E com isso, temos o nosso banco modelado de maneira adequado à forma como ele será utilizado neste micro serviço. Obviamente existem dezenas, se não centenas, de formas diferentes de modelar este banco, mas já temos um bom começo!

Curso Node.js e MongoDB
Curso Node.js e MongoDB

#3 – Subindo o banco e conectando

 

Agora que definimos o schema com o qual iremos trabalhar, vamos subir nossa instância de banco de dados, lembrando que localmente devemos usar outra porta que não a 27017 para não dar conflito com o banco do outro microserviço. Sendo assim, suba sua instância de mongod com o comando abaixo (considerando que o seu terminal está apontando para a pasta bin do MongoDB):

Note que no Windows você apenas digita ‘mongod’ (sem o ./ no início) e que nesse SO o seu caminho até a pasta ‘data’ irá iniciar com ‘C:\’ou equivalente, preenchendo todo o caminho até chegar na pasta ‘data’ dentro de cinema-catalog-service.

Agora que temos a instância de MongoDB deste microservice funcionando, vamos nos conectar nele através do utilitário ‘mongo’ passando a porta correta:

E depois vamos nos conectar no novo banco que deve ser criado:

Para em seguida podermos adicionar uma carga de dados, como abaixo (essa carga de dados encontra-se no zip do projeto, na pasta ‘config’, que você pode baixar no final deste post). Antes de você copiar e colar o trecho abaixo no seu console, preste muita atenção aos campos ‘idFilme’, substitua o ObjectId passado ali pelo ObjectId do banco movies-service do seu outro microservice:

Agora que temos dados populados na nossa coleção ‘cinemas’, vamos configurar nosso arquivo de variáveis de ambiente, .env, para apontar a string de conexão do módulo mongodb.js para a instância de servidor correta:

Note que defini a porta do servidor de Mongo para 27018, e que já aproveitei para deixar a variável de porta do Express já definida como 3001, visto que a 3000 está ocupada com o movies-service.

Será que nosso mongodb.js já está funcionando com o novo banco? Que tal rodarmos os testes unitários que possuímos para ele?

Crie na raiz do seu projeto o arquivo index.test.js com o seguinte conteúdo dentro:

Note que já coloquei os testes do módulo server também, já que a porta dele está configurada no .env também. E em nosso package.json, ajuste o script de test para apontar para nosso índice de testes, como abaixo:

Agora no seu terminal, navegue até a pasta src do projeto e rode um ‘npm test’ para ver o resultado dos nossos testes:

MongoDB e Server OK
MongoDB e Server OK

Assim, já temos a garantia que dois dos nossos módulos básicos já estão funcionando!

#4 – Iniciando o Repositório

Agora que temos o banco de dados e o servidor, que tal programarmos o repositório? Mas que funções precisaremos ter nele?

Isso depende do poder que queremos dar aos nossos usuários no front-end. Que tal começarmos com as possibilidades abaixo?

  • pesquisar cidades em que a rede possui cinema;
  • pesquisar cinemas por id da cidade;
  • pesquisar filmes disponíveis em um cinema;
  • pesquisar filmes disponíveis em uma cidade;
  • pesquisar sessões disponíveis para um filme em uma cidade;
  • pesquisar sessões disponíveis para um filme em um cinema;

Com isso, habilitaremos uma usabilidade potencialmente boa para quem for consumir nossa API. Sendo assim abra o seu arquivo cinema-catalog-service/src/repository/repository.js e adicione as três funções mais simples de todas: getAllCities, getCinemasByCityId e disconnect, sendo que esta última é opcional, só serve para os testes unitários:

Note que não há nada demais nas funções acima que mereçam grandes explicações. Na primeira função usei um projeção, que talvez seja algo novo para você: no segundo parâmetro do find podemos passar quais campos que queremos retornar na consulta, sendo que o padrão é retornar todos (equivalente a um ‘SELECT *’).

Já na segunda função ao invés de jogar o callback diretamente no toArray eu optei por tratá-lo melhor, passando adiante apenas o array de cinemas ao invés do documento inteiro de cidade, facilitando a vida de quem for consumir esta função mais tarde.

Vamos escrever os testes unitários destas três funções?

Crie um arquivo /cinema-microservice/cinema-catalog/src/repository/repository.test.js e coloque o seguinte código de teste dentro dele:

Se você fez toda o microservice anterior à este (o movies-service), já deve estar ‘careca’ de saber como funcionam os testes unitários via Tape. Aqui não há nada de diferente do que já fizemos na outra ocasião, apenas atente ao fato de que deixei uma variável movieId sem uso neste momento, mas que precisaremos mais tarde.

Para poder rodar este teste, vamos adicioná-lo no nosso index.test.js como manda o trecho abaixo:

Isso lhe permitirá testar o que fizemos até agora do repository.js com o comando ‘npm test’.

Se tudo deu certo você deve ter recebido mensagens positivas nos testes e agora é hora de criar as funções mais avançadas do nosso repositório, que nos exigirão uma série de conceitos novos, principalmente de agregações em MongoDB.

Mas isso fica para a parte 5 da série!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Arquitetura de micro serviços em Node.js + MongoDB: Parte 3

E chegamos à terceira parte da nossa série de artigos sobre como implementar na prática um projeto de sistema usando arquitetura de microservices usando Node.js e MongoDB.

Na primeira parte desta série eu dei uma introdução teórica sobre microservices e porque você deveria estar olhando para esta arquitetura e principalmente para as tecnologias Node e Mongo para implementá-la. Finalizei este artigo dando um case de exemplo que usaríamos para desenvolvimento ao longo dos outros artigos.

Na segunda parte, começamos a estruturar nosso projeto, definindo camadas, serviços e responsabilidades. Na sequência modelamos o nosso banco de dados, criamos o módulo de conexão e o módulo de acesso a dados (repositório), tudo isso usando configurações através de variáveis de ambiente (com dotenv-safe) e testes unitários com Tape.

Nesta terceira parte vamos finalmente finalizar o desenvolvimento do nosso primeiro microservice, o movie-service, que fornecerá acesso a consultas de filmes por ID, filmes que são lançamento e todos os filmes de maneira genérica. Lembrando que este serviço será utilizado por outro que fará a interface com a aplicação propriamente dita, como ilustrado pelo diagrama abaixo.

Consulta de Lançamentos do Cinema
Consulta de Lançamentos do Cinema

Veremos neste artigo:

  1. Programando o servidor
  2. Programando a API
  3. Programando o Index

Então mãos à obra!

Curso Node.js e MongoDB
Curso Node.js e MongoDB

#1- Programando o servidor

Agora é hora de programarmos os comportamentos da nossa API, mas antes disso precisamos construir nosso servidor.

Qualquer um que já leu alguma coisa a respeito na Internet sabe que este é o ponto forte do Node.js. É possível construir servidores web muito facilmente com Node a partir de 12 linhas de código.

Como cada micro serviço deve rodar standalone, sem depender de outros, é extremamente interessante que cada um tenha o seu próprio módulo de server.js para ser instanciado isoladamente.

O conteúdo do server.js pode ser visto abaixo:

Este servidor é genérico e simples, com uma função para iniciá-lo e outra para encerrá-lo. Ele usa o pacote morgan para logging de requisições no terminal/console e o helmet para garantir a proteção contra 11 ataques diferentes que sua API pode sofrer quando ir para produção e estar à mercê de hackers.

A função start espera a api, que vamos construir na sequência, o repositório, que já construímos e um callback que é disparado após a inicialização do servidor ser concluída. A api em si é que faz a magia de definição e tratamento das requisições em rotas específicas, tal qual já fizemos em outros tutoriais de Express aqui do blog.

Seguindo a nossa linha de ter unit tests para cada módulo do nosso projeto, vamos criar dentro da pasta cinema-microservice/movies-service/src/server um arquivo server.test.js contendo os testes abaixo:

Nestes testes nós iniciaremos o servidor usando uma API mockada (fake) e depois encerraremos este mesmo servidor. Bem simples, apenas para saber se ele está de fato subindo e sendo encerrado com sucesso.

Você pode rodar este teste isoladamente executando este arquivo com o comando ‘node server.test’ ou então adicionar uma nova linha no nosso índice de testes:

E com isso você já consegue garantir também que nosso servidor está funcionando, rodando um ‘npm test’ no console e vendo os resultados de todos os testes criados até o momento.

Testes de Servidor Ok
Testes de Servidor Ok

E agora, vamos finalmente criar a API em si?

Curso Node.js e MongoDB
Curso Node.js e MongoDB

#2 – Programando a API

 

Agora que temos o banco de dados, o repositório, o servidor e uma bateria de unit tests garantindo que tudo está funcionando como deveria, é hora de programarmos a API.

Na arquitetura desenhada até o momento, temos o arcabouço de servidor que espera que seja plugado um repositório e uma API. O repositório já temos pronto e a API vamos criar agora, dentro do que o servidor espera.

Para isso, dentro da pasta cinema-microservice/movies-service/src/api, crie um arquivo movies.js com o conteúdo abaixo:

Note que uma vez que grande parte do trabalho já foi segregado em outros módulos, coube ao módulo da API em si uma responsabilidade bem específica e ao mesmo tempo pequena, que é o tratamento das requisições.

Para garantir que esta nossa API está funcionando, vamos criar um movies.test.js na mesma pasta api para criarmos os testes abaixo:

Esse arquivo de teste ficou bem complicado, afinal, para conseguir testar nossa API temos de subir um servidor, conectar o repositório ao banco e usar uma biblioteca chamada supertest (não esqueça de rodar um NPM install) para simular as chamadas HTTP e com isso verificar se tudo está sendo retornado nos três endpoints como deveria.

Para garantir que os testes só vão rodar após o servidor ter subido, coloquei os testes dentro do callback do server.listen. Não vou entrar em detalhes do supertest aqui pois já falei dele no post de TDD em Node. Adicione mais uma linha no arquivo index.test.js e rode com um npm test para ver o resultado com seus próprio olhos.

API Ok
API Ok

Agora que temos a nossa API pronta e funcionando, vamos atar tudo no arquivo index.js do projeto movies-service.

#3 – Programando o Index

Como última etapa para fazer a nossa API de filmes funcionar, temos de orquestrar todos os módulos que compõem a API no arquivo index.js, pois é ele que será chamado para startar o nosso microsserviço quando colocarmos ele em um servidor (preferencialmente em um container Docker, mas isso é outra história).

Na verdade, uma vez que chegamos até aqui, com essa quantidade de testes e arquitetura de micro serviços bem definida, o index.js ficará tão simples quanto abaixo:

Se você duvida que é apenas isso, experimente rodar este index através de ‘node index’ ou ‘npm start’ e você vai poder testar, seja via POSTMAN ou diretamente no navegador mesmo.

Api funcionando no navegador
Api funcionando no navegador

Algumas boas práticas relacionadas a construção de APIs RESTful podem ser encontradas neste ótimo post (em inglês), mas te garanto que com esse basicão que vimos aqui você já está bem preparado para construir coisas mais poderosas e em cenários reais.

E não se preocupe, com o que lhe mostrei neste artigo conseguimos concluir apenas o primeiro microservice do nosso sistema completo. Quero que no mínimo consigamos avançar para o funcionamento do segundo microservice para fazer o fluxo básico de consultar filmes em cartaz em um cinema específico de uma cidade.

Mas isso fica para o próximo post da série!

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!