Being MEVN + Restful — Parte 2

Parte 2/3 de como construir uma aplicação MEVN + Restful API com deploy na Heroku e GitHub Pages!

Julio Lozovei
Training Center

--

Continuando com nosso tutorial, iremos agora montar o client-side da nossa aplicação, e iremos utilizar o Vue.js para isso.

Se você não viu a introdução ou a parte 1, dá um conferes lá!

Mas, por que Vue?

Porque ele é muito bom, e simples! O Vue é um framework JavaScript progressivo, que pode ser utilizado também como uma lib. Assim como o React, ele também faz uso do virtual DOM, possibilita a criação de componentes e mantém o foco em rotas e no gerenciamento do estado global (assim como o Redux para o React, temos o Vuex para oVue); e também, ambos trabalham de forma reativa.

É importante mencionar que em 2017 o repositório do Vue no GitHub foi o 6º com maior número de forks (fonte: https://octoverse.github.com/). Se você for procurar nos trending repositories no GitHub, verá que diariamente o Vue e diversas extensões para ele estão nessa lista!

Na minha opinião, por conta da sua sintaxe e ecossistema, o Vue possui uma curva de aprendizagem menor, quando comparado ao React. Depois, dê uma generosa olhada nessa página, onde a equipe do Vue faz uma comparação direta com outros frameworks — spoiler alert: as comparações são feitas entre Vue com React, Angular, Ember, Knockout, Polymer e Riot.

A seguir, vou deixar aqui um vídeo, do Gregg Pollack, falando um pouco sobre o framework e suas utilizações:

(áudio em inglês, mas tá legendado!)

Esse vídeo também está disponível no site do Vue: https://vuejs.org/

E também, para os curiosos que gostaram da ideia do Vue, deixo aqui uma excelente referência para estudos sobre os poderes do Vue.js:

Vue School: https://vueschool.io/

Enfim. Dito tudo isso, vamos continuar codando!

1 — Criando e clonando o repositório

Primeiramente, precisamos criar o projeto! Assim como na primeira parte, é bom que você crie um novo repositório no seu GitHub. Depois de criar, você deverá cloná-lo em seu HD.

Você também poderia manter as duas aplicações dentro do mesmo repositório (até mesmo utilizar server-side rendering), mas para esse projeto, iremos dividir as duas partes do projeto para ter um exemplo mais real world, onde duas ou mais aplicações irão ser integradas através dos web services, que foram feitos na parte 1.

2 — Iniciando o projeto com vue-cli

Para criar projetos com Vue, eu gosto muito de utilizar o vue-clié uma ferramenta CLI que automatiza a criação dos projetos. Através dela, você pode configurar, por exemplo, qual o tipo de teste e qual framework você irá utilizar, se você quer utilizar algum tipo de lint no seu código, qual tipo de processamento de CSS você irá utilizar…

Enfim, ela é muito útil e fácil de usar! Para isso, primeiro você deverá instalar ela como uma dependência global na sua máquina — dessa forma, sempre que você precisar criar um projeto com Vue, ela estará disponível. Para instalar, insira o seguinte comando no seu bash:

npm install -g @vue/cli

E, para criar o projeto, use o seguinte comando:

vue create <meu_projeto>

Onde, <meu_projeto> deverá ser substituído pelo nome do seu projeto — para o nosso exemplo, eu utilizei node-api-client.

Pelo vue-cli você pode escolher configurações padrão para projetos ou customizar a combinação dessas features. Quando você cria um novo projeto, você pode salvar a configuração escolhida como um pattern para utilizar em projetos futuros. Para esse projeto, especificamente, eu utilizei as seguintes configurações:

- Features: Router + Vuex + CSS Pre-processors + Linter/Formatter + E2E Testing- CSS pre-processors: SCSS/SASS- Linter: ESLint + Prettier com "Lint on save"- Testing: e2e com Cypress- Configurations in dedicated files

3 — Configurando o temido lint

O objetivo básico de todo e qualquer lint é melhorar a qualidade do código escrito. Isso é muito importante em projetos realizados em equipes, onde diversos desenvolvedores compartilham a mesma code base e cada um possui uma maneira única de escrever código.

Cada lint possui um conjunto de regras pré-definidas, com a grande maioria sendo opcional ou customizável. Através de arquivos específicos de configuração podemos escolher e definir quais regras iremos utilizar — esse é um ponto muito importante para ser definido em conjunto com toda a sua equipe de desenvolvimento.

Durante a criação do nosso projeto, escolhemos utilizar o eslint + prettier para nos ajudar com esse processo. O eslint irá fazer as validações das regras e o prettier irá ajustar nosso código quando salvarmos cada modificação — eu utilizo o plugin JsPrettier para o Sublime Text, assim o prettier irá utilizar as regras que eu defini e, quando eu utilizar o plugin manualmente, ele irá formatar meu código.

Para o nosso projeto, vamos utilizar uma configuração mais básica. Comparando com os arquivos originais (criados com o vue-cli), apenas alterei a regra curly do eslint e adicionei as regras para o prettier:

Configurações do eslint
Confirugações do Prettier

4 — Textos centralizados e Internacionalização

Utilizar textos hard coded dentro de qualquer sistema não é considerado uma boa prática — isso te impede de, por exemplo, criar algum tipo de internacionalização baseada na localização do seu usuário.

Para resolver esse problema, antes de começarmos a criar os nossos componentes, vamos criar um arquivo específico apenas para guardar todos os textos que teremos no nosso projeto, como por exemplo o texto dos botões e links, mensagens de erro, placeholders…

Para isso, eu criei um novo diretório, src/utils/ que irá armazenar o arquivo strings.js, onde estarão todos os textos que utilizamos em nossos componentes:

src/utils/strings.js

Através desse arquivo, quando precisarmos alterar qualquer string, ficará tudo mais fácil — dentro do strings.js poderia também ser feita a adição de idiomas, agrupando os diversos textos para criar traduções dinâmicas.

Agora, dentro dos nossos componentes, podemos importar esse arquivo e utilizar os conteúdos de maneira mais organizada.

Além desse tipo de arquivo, um diretório utils poderia armazenar funções para manipulação de DOM, por exemplo.

Apenas para citar, vários frameworks JavaScript possuem packages para realizar a internacionalização de projetos — um dos mais utilizados para o Vue é o vue-i18n. Você também pode utilizar templates como o vue-boilerplate, que já vem com o módulo de internacionalização embutido.

5 — Criando a árvore de componentes

O vue-cli cria um "hello world app". Com isso, nós podemos aproveitar a arquitetura de diretórios gerado por ele para entender como o ecossistema do framework irá funcionar:

Estrutura básica criada através do vue-cli

Pontos para serem analisados:

  • Todos os arquivos de desenvolvimento ficarão em /src/
  • Em src/assets/ se concentram arquivos de estilo globais e imagens
  • Todos os nossos componentes ficarão em src/components/ — aqui, para organizar melhor, vamos criar mais diretórios para dividir os tipos de componentes que teremos
  • Por padrão, em src/views/ ficam os grupos de componentes como, no nosso caso, a Home
  • O arquivo App.vue será o nosso container principal — dentro dele que iremos chamar o <router-view />, que faz com que a renderização seja feita através da rota que estamos
  • main.js é onde está a nossa instância do Vue, junto com os plugins que vamos utilizar
  • routes.js será o nosso arquivo de rotas
  • store.js é a nossa instância do Vuex, que trabalha com o conceito de store.

Por esse projeto ser bem simples, acabei optando por deixar o Vuex um pouco de lado e focar nos componentes + eventos.

5.1 — Componentes de Layout

Desses componentes que foram gerados automaticamente, iremos apenas utilizar o App.vue. Eu gosto de deixar separado os componentes que são apenas para compor o layout da aplicação — por exemplo, o header e o footer agrupados em /layout/, separados do restante.

Então, dentro de src/components/ iremos criar um diretório, /layout/ e iremos também adicionar os componentes Header.vue e Footer.vue. Nessa pasta, também poderíamos deixar outros elementos, como o menu e alguns widgets como alertas, snackbars, toasters…

5.2 — Componentes de "publicação"

Para manter a organização, todos os componentes que compõem o layout e estrutura das nossas "publicações" vão ser agrupados no diretório /publication/.

As publicações vão ser compostas por 3 partes — cabeçalho, corpo e rodapé. Poderíamos deixar as 3 partes em apenas um componente, Publication.vue; porém, pensando em uma futura manutenção mais fácil, iremos dividir essas três partes em componentes diferentes: Header.vue, Body.vue e Footer.vue, que serão importados dentro do componente Publication.vue.

Árvore final de componentes do nosso projeto

5.3 — Rotas

Para que consigamos acessar o componente correto de acordo com a rota, o Vue nos oferece o vue-router. Assim como configuramos as rotas para os nossos endpoints na parte 1, iremos configurar as rotas do nosso front-end para que o usuário consiga acessar a informação desejada.

Devido a simplicidade do nosso projeto, teremos apenas 1 rota, que será a "index". A declaração de rotas no Vue é bem simples:

routes: [
{
path: '/',
name: 'home',
component: Home
}
]

Onde:

  • path é o caminho da URL que o usuário deverá acessar;
  • name é o nome interno dessa rota;
  • component é a referência de qual componente deverá ser renderizado quando o usuário acessar a rota

Com o vue-router também podemos criar subrotas, por exemplo: /user/profile e /user/feed .

Nas documentações do vue-router tem uma sessão que fala exatamente sobre isso: vue-router nested routes.

6 — Conectando o client-side com o server-side

Para fazer a conexão entre o client e o server-side iremos utilizar o axios, um client HTTP que funciona muito bem com qualquer lib/framework. Tudo dentro do axios é uma promise, então isso já é uma boa razão para utilizá-lo.

Outro ponto bacana do axios é que você pode configurar os parâmetros que estarão presentes em todas as requisições — como a base da URL, os headers de auth; para quem já usou jQuery, basicamente é o jQuery.ajaxSetup().

6.1 — Confirugrando axios

Para configurarmos as propriedades padrão que iremos utilizar no axios, vamos criar um novo diretório, src/components/services/ para organizar tudo. Dentro de /services/, vamos ter 3 arquivos:

  • api.js, que será a instância global do axios;
  • keys.js, que irá armazenar as constantes utilizadas na configuração do axios;
  • publications.js, onde estarão os métodos que retornam as nossas requests

6.2 — Chamando API nos componentes

Como nós armazenamos todos os métodos que precisamos em src/components/services/publications.js, dentro dos nossos componentes precisamos importar esse arquivo para realizar qualquer request.

Mantendo a organização do projeto, iremos importar as requests em apenas 2 componentes: no Form.vue e no Feed.vue, e também na view Home.vue. Dessa forma, apenas estes componentes terão regras de negócio sendo processadas, enquanto os outros ficarão com uma estrutura e instruções mais simples.

Assim, se precisarmos realizar qualquer request dentro dos outros componentes, podemos utilizar o conceito de event emitters do Vue.

6.3 — Event Emitters

Event Emitters nada mais são do que eventos emitidos entre componentes. Em outras palavras, um componente filho emite um determinado evento para o componente pai, que irá executar uma determinada instrução.

Essa abordagem é utilizada em componentes que são utilizados em diversos lugares da aplicação. Vamos pegar de exemplo um componente de botão, que irá renderizar um <button> na página. Necessariamente, esse componente não precisa ter todos os possíveis métodos que irá executar; podemos passar esse evento como uma propriedade (quase como uma função em callback). Quando o botão for clicado, irá emitir uma notificação para o seu componente pai, que por sua vez receberá essa notificação e executará o evento original.

Vamos utilizar esse tipo de situação em alguns componentes da nossa aplicação para:

  • consultar publicações
  • favoritar uma publicação
  • reportar uma publicação

É importante ressaltar que, como ainda estamos no ambiente de desenvolvimento, para que você consiga realizar as requests entre o back-end e o front-end do nosso projeto, você precisa estar com o servidor do Mongo e o código do back-end rodando!

Na próxima parte iremos ver como realizar o deploy das duas aplicações, junto com o servidor do banco de dados. Aí sim, você não precisará se preocupar em rodar todos os servidores para que as requests funcionem.

7 — Estilizando os componentes

Agora vem a parte divertida do front-end — estilizar os componentes. Por padrão, a versão mais nova do vue-cli permite que você utilize o PostCSS junto com o pré processador que você escolheu quando criou o projeto.

Até agora, nós criamos alguns estilos bem básicos apenas para melhorar a apresentação dos componentes na tela. Porém, precisamos utilizar uma arquitetura de CSS para deixar tudo mais organizado.

7.1 — Design System + reset.css

Quando vamos desenvolver qualquer tipo de aplicação que envolva a interação do usuário, precisamos definir sua identidade visual — a maneira como esses usuários vão assimilar a nossa aplicação e, posteriormente, também reconhecê-la. Um exemplo muito simples disso é a logo do Google:

você sabe quais são as cores utilizadas?

Podemos não saber a ordem exata das cores (embora vemos ela muitas vezes ao dia, quase todos os dias), mas sabemos que tem azul, vermelho, verde e amarelo no meio. De uma maneira simples, isso é um design system; tanto a logo, quanto a aparência do sistema.

Em uma definição mais técnica:

Design System é um guia que busca o benefício de softwares, comunicações, pessoas e negócios.

Uma excelente referência para você ler depois é esse post sobre design systems, do Amilton L. Paglia.

Enfim, voltando ao foco da nossa aplicação, precisamos definir uma identidade visual básica e também um reset consistente.

A função de um reset.css é limpar toda a formatação que os browsers definem como "padrão" — por exemplo, o estilo das listas com dots ou numerais, paddings e margins em determinados elementos. Basicamente, é remover a formatação original e definir os seus padrões, que serão comuns para os diversos elementos que você irá utilizar.

Existem algumas libs que fazem esse trabalho, como o normalize.css, mas você também pode criar o seu próprio. Nesse link você pode encontrar alguns exemplos de CSS reset muito utilizados.

Eu, particularmente, gosto de usar o meu próprio reset.css, pois consigo ter mais controle sobre quais elementos e propriedades estarei resetando.

Vou deixar aqui um exemplo bem básico para um reset.css — inclusive, estaremos utilizando ele no nosso projeto:

reset.css (bem) básico

7.2 — Arquitetura e Metodologia

Existem diversas metodologias para organizar o CSS de um projeto — OOCSS (CSS orientado a objetos), ITCSS (triângulo invertido do CSS), SMACSS (CSS em camadas), Atomic CSS

Todas elas possuem um conjunto de regras (diferentes ou não) mas um mesmo objetivo em comum — manter seu CSS organizado para que você não se perca. É muito fácil começar a escrever um CSS legível e sustentável, porém é mais fácil ainda transformar ele em um imenso iceberg; ainda mais trabalhando em equipe, onde cada um possui um jeito único de codar.

Isso é o que queremos evitar

Outro objetivo dessas metodologias, seja especificamente para CSS ou não, é fazer com que o código tenha a aparência de que foi escrito por apenas uma pessoa — sim, pode não fazer muito sentido isso porém é verdade.

Fazendo uma comparação meio estranha, pense em um código como se fosse um carro: não faz sentido nenhum cada parte do carro ter uma aparência diferente das outras; tudo tem que estar dentro de um determinado padrão, em consonância. Além de ser estranho um carro ter cada roda com um tamanho diferente, a performance desse carro seria seriamente afetada.

Voltando para o nosso projeto, vou deixar esse passo específico em branco, dando liberdade para você escolher o que fazer.

Eu apenas criei um estilo bem básico para que as informações fossem mostradas na tela de maneira mais amigável para o usuário, utilizando o ITCSS aliado ao padrão de nomenclatura BEM.

Basicamente, o ITCSS divide o CSS em um triângulo invertido:

Estrutura básica do ITCSS

Onde:

  • settings, ou configurações, é onde ficarão as definições básicas da arquitetura. Variáveis de cores, espaçamentos e tamanhos ficam nessa camada;
  • tools, ou ferramentas, é onde ficam armazenados os mixins, font-faces e a construção de animações (keyframes);
  • generic, ou genéricos, é a primeira camada que irá aplicar estilo: aqui geralmente ficam os resets e propriedades com a menor especificidade;
  • base (elements), ou base, é onde são aplicadas as estilizações básicas; é a última camada onde aplicamos estilo utilizando os seletores das tags HTML;
  • objects, ou objetos, é onde criamos o padrão visual dos elementos (botões, tipografia, listas…). A partir desse ponto, a aplicação dos estilos deverá sempre ser feita com seletores de classes;
  • components, ou componentes, é onde os estilos são mais específicos. Nesse ponto criamos os componentes que o usuário irá ver, como uma lista de produtos, o header e elementos que irão compor o layout da nossa aplicação como um todo;
  • trumps, também chamado de utilities ou helpers, é a camada responsável pelos estilos mais específicos, alguns podendo até utilizar !important . A principal aplicação dos trumps são as classes reativas, onde queremos que aquele estilo seja aplicado independente do que esteja acontecendo na nossa aplicação.

Mas, você pode seguir outra metodologia, ou então utilizar um framework para CSS, por exemplo o Bootstrap ou o Foundation. Inclusive, falando em Bootstrap, existe uma versão para Vue: o Bootstrap Vue!

8— Testes

Para realizar testes na camada da view das aplicações, geralmente utilizamos a abordagem e2e (end to end). Nessa abordagem, iremos testar o fluxo de navegação da aplicação.

Ou seja, através de um teste automatizado iremos simular o comportamento do usuário utilizando a nossa aplicação. Para isso, vamos utilizar o cypress.io, que é uma biblioteca de testes e2e completa e de fácil uso.

Há algum tempo atrás, uma das libs mais completas de testes e2e era o nightwatch.js, porém o nightwatch dependia do Selenium para automatizar o browser durante o processo de testes. O cypress surgiu como uma alternativa robusta pois não depende do Selenium — sua arquitetura é completamente diferente e não segue as mesmas restrições.

Enfim, o código dos testes do front-end seguem a mesma estrutura dos testes do back-end; temos um describe que será o conjunto e cada unidade é definida por um it(); dentro de cada unidade de teste, temos o prefixo cy para acessarmos os métodos do cypress.

Iremos escrever testes que irão simular o fluxo do usuário durante a criação de uma nova publicação (validando o formulário), e nos atos de favoritar e reportar uma publicação.

O código de testes deverá ficar, mais ou menos, dessa maneira:

Onde:

  • o primeiro teste irá validar o formulário, verificando se o texto de erro da mensagem possui a classe .active;
  • o segundo teste irá validar a criação de uma nova publicação, verificando se a mensagem inserida é a mesma do primeiro item do feed;
  • o terceiro teste irá validar quando o usuário favoritar a publicação — como a publicação de teste não possui nenhuma estrela, se após o clique o parágrafo conter 1, significa que a request deu certo;
  • o quarto teste irá validar o report (exclusão) de uma publicação — se o id do segundo item da lista for, após o clique no link de report, igual ao id do primeiro, significa que a request deu certo.

O cypress oferece vários tipos de testes com assertions, vale a pena dar uma olhada nas documentações!

9 — Conclusão

Chegamos ao final da segunda parte do tutorial. Agora, você já tem em mãos um belo MVP de uma API em Node (client + server), parabéns!

Nessa parte eu foquei mais em passar alguns conceitos do que mostrar os códigos; mas, todos os códigos estarão disponíveis nesse repositório que criei especialmente para o tutorial e os (poucos) códigos que foram mostrados aqui na publicação também estarão salvos (perdidos) lá nos meus Gists

Na próxima (e última) parte, iremos fazer o deploy desses códigos na nuvem, utilizando a Heroku para hospedar a parte do back-end e o GitHub Pages para hospedar e renderizar o front-end da nossa aplicação.

Nos vemos lá!

--

--

Julio Lozovei
Training Center

Human, front-end developer, amateur musician, writer and speaker; problem solver. https://jlozovei.dev