Criando uma aplicação em NodeJS, com Express e Handlebars - Parte 2

Aprenda a desenvolver uma aplicação, website, passo a passo em NodeJS, utilizando o Express, Handlebars, e servindo a aplicação online no Heroku.


Este artigo faz parte de uma série de tutoriais que ensinarão passo a passo desde o desenvolvimento até a publicação de um pequeno site de apresentação utilizando o NodeJS e suas ferramentas.

Basicamente um mini curso. Seguindo todos os passos, no final você vai:

  • Saber como instalar e configurar uma aplicação NodeJS.
  • Servir uma aplicação online através do Express.
  • Utilizar Handlebars para fornecer uma interface visual para o usuário com HTML e CSS.
  • Publicar o seu site online no Heroku para que possa ser acessado por qualquer pessoa. Como este de exemplo aqui: Projeto Website

Artigo anterior: Criando uma aplicação em NodeJS, Express e Handlebars - Parte 1

Seja bem-vindo. E bons estudos.


Introdução

No final deste artigo você será capaz de criar páginas para o seu site de duas maneiras diferentes, disponibilizar e consumir arquivos estáticos, conhecer conceitos e implementações de view engine, além de outros conceitos importantes para o seu progresso como desenvolvedor.

É possível servir uma mesma página web de diferentes maneiras com o NodeJS. Neste artigo apresentarei duas abordagens muito utilizadas. A primeira é mais fácil e prática, porém possui algumas limitações, sendo ideal para projetos mais simples. A segunda maneira é utilizando a View Engine do Express, permitindo uma maior customização, processamento de dados no servidor, criação de templates, entre outros.

Primeiramente, para manter o nosso projeto organizado faremos algumas modificações em nossa pasta. Criaremos uma pasta "public" na qual colocaremos os arquivos estáticos (css, js do cliente, e imagens), e uma pasta "views" onde colocaremos os arquivos relacionados as páginas html e view engine.

A estrutura ficará assim:


Modelo 1 - Disponibilizando uma página HTML

Para disponibilizar uma página HTML básica é muito simples, basta utilizar o "sendFile", um método do Response que tem como função enviar um arquivo para o cliente, que por sua vez também irá configurar o tipo de conteúdo HTTP com base na extensão do arquivo. Ou seja, ao enviarmos um arquivo com extensão ".html", automaticamente o arquivo será interpretado como uma página HTML.

Criaremos na pasta "views" um arquivo "index.html". Este arquivo será a página principal do nosso projeto. Se assim desejar, no arquivo recém criado, pode copiar e colar o exemplo da página a seguir:

<!doctype html>

<html lang="pt-br">
<head>
  <meta charset="utf-8">
  <title>Criando uma aplicação NodeJS</title>
  <meta name="description" content="Criando uma aplicação NodeJS">
  <meta name="author" content="JSagon">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" />

  <style>
      html, body {
        min-height: 100%;
      }
      body {
        font-size: 14px;
        line-height: 25px;
        margin: 0px;
        font-family: open sans,Arial,sans-serif;
        -webkit-font-smoothing: antialiased;
				color: #313131;
      }
      
      h1 {
        font-weight: 600;
        font-size: 45px;
        margin: 0 0 5px;
        line-height: 75px;
      }
      h4 {
        font-weight: 500;
        margin: 10px 0px 10px 0px;
        font-size: 16px;
        line-height: 30px;
      }
      .header {
          margin: 120px;
          text-align: center;
      }
  </style>
</head>
<body>
  <main class="container">
      <div class="header">
	      <div class="title"><h1>JSagon</h1></div>
	      <div class="sub-title"><h4>Criando uma aplicação NodeJS.</h4></div>
	      <div><h4>"Bom mesmo é ir à luta com determinação, abraçar a vida com paixão, perder com classe e vencer com ousadia, porque o mundo pertence a quem se atreve e a vida é muito curta, para ser insignificante" — Charlie Chaplin</h4></div>
	    </div>
  </main>
</body>
</html>

Com o arquivo da página principal criado, agora modificaremos a declaração da nossa rota raiz "/" para retornar o arquivo em questão.

Partindo do artigo anterior, nosso arquivo server.js ficará da seguinte maneira.

const express = require('express')
const app = express()

app.get('/', (req, res) => {
    // envia o arquivo da página principal
    res.sendFile(__dirname + '/views/index.html')
})

app.get('/sobre', (req, res) => {
    res.send("

Um simples tutorial de NodeJS.

") }) app.listen(3000, () => { console.log('Server online') })

Confira se a aplicação está online, caso contrário, rode o "npm run dev". E veja o resultado em "localhost:3000"

Você deverá ver um título e texto centralizados no meio da tela. Caso não esteja aparecendo, ou tenha dado algum erro, verifique os passos anteriores.

Obs.: No final do artigo deixarei uma página mais bonitinha para ser customizada e podermos postá-la no Heroku.

O sendFile recebe como parâmetro um caminho absoluto do arquivo a ser enviado. Caminho absoluto é o caminho completo desde a raiz do seu sistema operacional. Para evitarmos digitar todo esse caminho, fazemos uso do __dirname, uma variável de ambiente do Node que já possui esse caminho até o arquivo em que foi declarado. Depois concatenamos com o caminho de nosso arquivo ".html".

Caso tenha reparado, temos declarações CSS no arquivo index.html, e isso não é recomendado. Não é uma boa prática. Para corrigir, criaremos dentro da pasta "public" um arquivo de nome "style.css" e passaremos toda a declaração para ela. O arquivo style.css ficará da seguinte forma:

html, body {
    min-height: 100%;
}
body {
    font-size: 14px;
    line-height: 25px;
    margin: 0px;
    font-family: open sans,Arial,sans-serif;
    -webkit-font-smoothing: antialiased;
    color: #313131;
}
h1 {
    font-weight: 600;
    font-size: 45px;
    margin: 0 0 5px;
    line-height: 75px;
}
h4 {
    font-weight: 500;
    margin: 10px 0px 10px 0px;
    font-size: 16px;
    line-height: 30px;
}
.header {
    margin: 120px;
    text-align: center;
}

Agora que criamos um novo arquivo para o CSS, precisaremos inserir uma tag de importação para carregá-lo junto com o nosso "index.html". Então entre as tags head, iremos inserir o style.css como a seguir:

<head>
  <!-- ... -->
	<link rel="stylesheet" href="/public/style.css">
</head>

Caso você resolva testar para ver o resultado, verificará que os estilos CSS não serão aplicados, e caso veja o console.log do browser, verá que ocorreu um erro ao importar o arquivo. Isto acontece porque para servir arquivos estáticos em NodeJS precisaremos primeiro fazer uma alteração em nosso código no "server.js".

Para que funcione, adicionaremos então o seguinte código logo após a declaração da instância de nossa aplicação.

//...
const app = express()
app.use('/public', express.static(__dirname + '/public'))
//...

Resumidamente, o app.use é utilizado para registrar middlewares (blocos de códigos) que serão executados em todas as requisições ou naquelas em que o critério informado for atendido. Neste caso, estamos definindo que quando houver uma requisição para a rota "public", será executado o middleware de fornecimento de arquivos estáticos apontando para a pasta public recém criada.

Caso seja feito o teste neste momento, a aplicação deverá funcionar corretamente apresentando o visual de acordo com o CSS.

Simples e rápido de se implementar. Caso tenha ficado alguma dúvida, ou algum erro tenha acontecido e não esteja conseguindo solucionar, entre em contato.


Modelo 2 - Utilizando View Engine

Avançaremos agora para o desenvolvimento utilizando view engines, um recurso muito interessante e importante a ser utilizado em aplicações mais complexas.

View Engine, ou Template Engine, permite uma maneira mais dinâmica de se trabalhar e servir páginas html. Existem várias opções notáveis, como Jade, Mustache, EJS, Handlebars, entre outros. Utilizaremos o Handlebars para implementação, sua sintaxe é simples e ele permite tratamentos interessantes em um desenvolvimento mais avançado.

Tendo isso explicado, comecemos então.

Primeiramente, precisaremos instalar o pacote do Handlebars que funciona especialmente com o Express. Rode o seguinte comando em seu terminal para instalá-lo:

npm i express-handlebars

Agora, configuraremos a aplicação para utilizar o Handlebars como nossa view engine. Nosso código no arquivo server.js com as mudanças anteriores ficará assim:

const exphbs  = require('express-handlebars');

//...
const app = express()
app.use('/public', express.static(__dirname + '/public'))

// define a extensão e a instância do handlebars com o modelo que será interpretado o código
app.engine('hbs', exphbs({extname: '.hbs'}));
// define qual o template a ser utilizado
app.set('view engine', 'hbs');

//...

Handlebars por padrão define a sua extensão como ".handlebars", porém como é uma extensão relativamente grande, normalmente se utiliza a sua abreviação por motivos estéticos, agilidade e melhor tratamento, ".hbs".

Certo, instalamos a view engine e já registramos a engine em nossa aplicação. Agora precisaremos criar alguns arquivos necessários. Na pasta "views", criaremos uma outra pasta chamada "layouts". Por padrão, a engine do handlebars irá procurar uma pasta de mesmo nome dentro da pasta "views", isso tem como ser alterado, mas por ser um nome adequado, manteremos assim.

Dentro da pasta "layouts" recém criada, criaremos agora um arquivo chamado "main.hbs". Este arquivo conterá o código HTML base de nossa aplicação. Como no exemplo a seguir:

<!doctype html>

<html lang="pt-br">
<head>
  <meta charset="utf-8">
  <title>Criando uma aplicação NodeJS</title>
  <meta name="description" content="Criando uma aplicação NodeJS">
  <meta name="author" content="JSagon">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" />
  <link rel="stylesheet" href="/public/style.css">
</head>
<body>
  <main class="container">
      
  </main>
</body>
</html>

Como você pode observar, o código é muito similar ao código criado do primeiro exemplo, exceto pelo "". Estas seis chaves, três abrindo e três fechando, é uma convenção de código utilizado pelo handlebars para inserir códigos html manipulados pelo "servidor".

Neste caso, este arquivo "main.js" é uma página de layout, é uma página base. E todas as páginas dinâmicas de nossa aplicação serão inseridas automaticamente no "body".

Façamos o próximo passo para que seja melhor entendido.

Dentro da pasta "views", em sua raiz, crie um arquivo de nome "index.hbs". Neste mesmo arquivo, insira o código a seguir:

<div class="header">
    <div class="title"><h1>JSagon</h1></div>
    <div class="sub-title"><h4>Criando uma aplicação NodeJS.</h4></div>
    <div><h4>"Bom mesmo é ir à luta com determinação, abraçar a vida com paixão, perder com classe e vencer com ousadia, porque o mundo pertence a quem se atreve e a vida é muito curta, para ser insignificante" — Charlie Chaplin</h4></div>
</div>

Como pode reparar, separamos em dois arquivos a página que criamos no exemplo inicial. Pode parecer neste momento, devido as mudanças e tudo mais, que o trabalho é mais desgastante que o primeiro. Porém você logo verá as vantagens de utilizar tal recurso.

Por fim, agora precisaremos modificar a nossa rota principal para renderizar a página index no novo modelo. O código ficará assim:

// ...
app.get('/', (req, res) => {
    res.render('index')
})
// ...

O método render utiliza da engine para renderizar a página em questão definida. Acesse o "localhost:3000" e você verá o resultado. Novamente, caso ocorra algum erro, verifique os passos anteriores com calma.

Explicando de forma simplificada, o que acontece é o seguinte: o render, e a engine, em tempo de execução recupera o bloco de código inserido no arquivo "index.hbs" e insere o mesmo bloco de código no html do "main.hbs" que se encontra em layouts, e envia o código para o cliente como se fosse um único código html.

Criaremos agora um novo arquivo na raiz da pasta "views" chamado "sobre.hbs". Este arquivo conterá o seguinte código:

<div class="header">
    <div class="title"><h1>Sobre</h1></div>
    <div class="sub-title"><h4>Esta é uma página de sobre.</h4></div>
</div>

Agora, modifique a sua rota "/sobre" para renderizar este arquivo recém criado da seguinte forma:

// ...
app.get('/sobre', (req, res) => {
    res.render('sobre')
})
// ...

Veja se a aplicação foi reiniciada, e acesse "localhost:3000/sobre" para ver o resultado.

No modelo 1, onde a página é servida como um arquivo através do "sendFile", sempre que fosse criado uma nova página, um novo arquivo, toda a estrutura iria ter que ser replicada. Imagine agora uma página com menu, rodapé, entre outros, a dificuldade que seria fazer uma mudança em todas elas quando necessário. Utilizando a view engine precisamos declarar o layout da página apenas uma única vez, e ela será reutilizada sempre que necessário, já que estará centralizada. Desta forma, qualquer mudança realizada no layout será automaticamente refletida.

Para finalizar, nosso código no server.js ficou da seguinte maneira:

const express = require('express')
const exphbs  = require('express-handlebars');

const app = express()
app.use('/public', express.static(__dirname + '/public'))

app.engine('hbs', exphbs({extname: '.hbs'}));
app.set('view engine', 'hbs');

app.get('/', (req, res) => {
    res.render('index')
})

app.get('/sobre', (req, res) => {
    res.render('sobre')
})

app.listen(3000, () => {
  console.log('Server online')
})

Nossos arquivos e pastas ficaram distribuídos como a seguir:


Próximo artigo: Criando uma aplicação em NodeJS, Express e Handlebars - Deploy Heroku - Parte 3


Considerações finais

Como prometido, você encontrará o código de uma página melhor elaborada neste link do projeto no Github.

Para o artigo não ficar muito longo, terminaremos por aqui. View Engines, e neste caso, o Handlebars, possuem diversas funcionalidades interessantes. Provavelmente estarei trazendo em artigos futuros, caso deseje saber ou pedir um novo artigo sobre, pode entrar em contato.

Para mais detalhes sobre implementação do hanblebars, segue o link de seu pacote oficial: https://www.npmjs.com/package/express-handlebars


Caso queira tirar alguma dúvida a respeito das tecnologias utilizadas, implementações ou contratempos que estejam tendo em implementações próprias. Pode entrar em contato clicando aqui.