Original de http://www.flickr.com/photos/xomiele/6759264721/sizes/o/in/photolist-bihZUV-88vMPx-fKyj49-9Ziey1-9Zfi2P-fh9yzh-duyXLZ-fnoVXN-9jAtYg-aoBkW8-a6g8PV-7PSnrq-aLcvgx-8VqoQE-dFPWyD-ddm4QY-8iMSCj-cx4JQ9-82Qi7p-dX3M6i-9Pyhsi-eFL7E3-8KXHJa-a4Z123-aUyizz-fA2tVo-8pd1UK-7LGh72-f7iSES-86LCeR-eexNKG-axknKc-9ZAgfy-f9eYHE-f8ZJBt-f9f5nb-f9f45W-f9f2Ww-eYZQQ9-8cA37G-9HfrPJ-avSGYF-e5ymJN/

No último post vimos como criar uma app com NodeJS e ExpressJS. Já temos a app rodando, mas não expliquei como o Express está respondendo com aquele conteúdo inicial:

O Express utiliza o Jade para exibir suas views. Esse é o view engine padrão, mas você pode trocar. Eu gosto do Jade, então vamos seguir com ele. Por convenção as views do Express ficam no diretório “views”. Veja o que temos nele:

$ ls views
index.jade  layout.jade

Quem já trabalhou com qualquer framework moderno já sacou que o index.jade é o responsável pela view que estamos vendo, e que o layout.jade é o arquivo de dá o layout de base para todas as views. É exatamente isso. Vejamos o conteúdo do index.jade:

extends layout

block content
  h1= title
  p Welcome to #{title}

E aqui o conteúdo do layout.jade:

doctype 5
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    block content

O Jade, assim como o CoffeeScript, o Python, e diversas outras linguagens, utiliza a identação para indicar escopo. Assim, no layout.jade, teremos o title debaixo de head, conforme esperado, e isso é indicado pelos dois espaços a mais que o title tem a sua esquerda. Simples assim. Jade, assim como CoffeeScript e qualquer linguagem que use significant white space (ou qualquer linguagem, na verdade), tem as pessoas que amam e as que odeiam. Eu gosto muito do uso de identação pra indicar escopo, e gosto do Jade. Se você não gostar você pode trocar por outro view engine, não é nada difícil e já vamos ver como fazer isso.

O Html gerado é a combinação das duas views. Veja no index.jade o “extends layout”, indicando que ela é uma view “filha” da view de layout, e veja também que as duas declaram “block content”. O que acontecerá é que o conteúdo do “block content” da index.jade substituirá o conteúdo do “block content” layout.jade (que no caso é vazio, mas não precisava ser). Aqui o html final:

<!DOCTYPE html>
<html>
<head>
  <title>Express</title>
  <link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
  <h1>Express</h1>
  <p>Welcome to Express</p>
</body>
</html>

O resultado é exatamente o esperado, sem surpresas. A única coisa que chama atenção é o conteúdo “Welcome to Express”, que substituiu “Welcome to #{title}” do arquivo index.jade. Obviamente há um contexto na exibição da view, e ele possui um atributo “title”. Vamos ver como ele foi parar lá.

Quando uma view é renderizada você pode passar pra ela um objeto que será o seu contexto. Isso é padrão do ExpressJS. O Jade, assim como diversos outros view engines suportam esse padrão: pegue os arquivos de view, e mescle com os valores de um objeto qualquer. E como o JavaScript é uma linguagem dinâmica você não precisa informar o tipo do objeto. Duck typing FTW.

De onde está vindo esse objeto? Note que há um diretório “routes” no projeto, e que ele possui um arquivo “index.js”. Esse arquivo possui o seguinte conteúdo:

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

Esse arquivo está exportando uma função chamada “index”, que recebe um request e um response como argumentos, e que renderiza com uma chamada a “res.render” a view “index”, e passa um objeto JavaScript anônimo que possui somente um atributo “title” com o valor “Express”, que é exatamente o valor exibido no html acima no “Welcome to Express”.

O método render do objeto de resposta vai enviar ao view engine o objeto de contexto, que por sua vez vai gerar a string final e retornar ao objeto response, que por sua vez vai enviá-la para o usuário. É isso, simple assim.

E onde esse método index é usado? Sabemos que ele responde à rota “/” (como em http://localhost:3000/). Como a rota “/” foi ligada a esse método? A resposta está no arquivo app.js, que configura toda a aplicação. Há algumas linhas muito importantes. Vamos ver ele novamente:

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

var app = express();

app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', routes.index);
app.get('/users', user.list);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

Na linha 2 vemos que ele requer “./routes”, que, como “routes” é um diretório vai ser resolvido automaticamente para “./routes/index.js”, que como já vimos está exportando o método “index”. Esse objeto que possui o método index é colocado, no arquivo app.js sob a variável de nome “routes”, ainda na linha 2.

Na linha 16 vemos “app.use(app.router);”. App.use é uma indicação ao Express de que ele deve usar um middleware, no caso da linha 16, o de rotas. Depois vou falar mais sobre middlewares.

E na linha 23 temos a indicação de a rota “/” deve ser mapeada diretamente ao método “routes.index”, que é o método “index” do arquivo “routes/index.js”. Isso é feito com a chamada ao método app.get. Se fosse para lidar com um post, seria app.post em vez de app.get. Existe também o app.delete, e o app.put, para atender as 4 operações do REST.

Temos na linha seguinte, a 24, outra rota, a “/users”. Experimente e veja o que ela faz. Não vou detalhar no post, fica de lição de casa para você entender o que acontece. 🙂

Na prática, qualquer rota pode ser ligada a qualquer método que receba dois argumentos, que sempre serão request, e response. No caso, em vez de toda essa configuração, a linha 24 poderia ser somente:

app.get('/', function(req, res){
  res.render('index', { title: 'Express' });
});

O resultado seria exatamente o mesmo e não precisaríamos do arquivo “routes/index.js”. Por convenção e por organização, no entanto, mantemos as rotas no diretório routes. Imagine que uma aplicação teria centenas, talvez milhares de rotas. Seria impossível manter tudo no arquivo app.js e ter uma aplicação organizada.

O arquivo app.js é executado uma única vez, quando a aplicação sobe. A partir daí somente os arquivos de rotas e views são executados, conforme o necessário. O app.js apenas configura a aplicação, e então sai de cena.

E pra fechar, veja nas linhas 10 e 11 a configuração do view engine. Na linha 10 ele informa onde as views estão (__dirname se refere ao diretório do arquivo atual, o app.js), e a linha 11 informa que estamos usando Jade. É assim que o Express sabe qual módulo do npm ele deve usar para renderizar as views.

Não vou mais me aprofundar em Jade, se você quer saber mais veja o site do projeto. Vou falar dele de vez em quando, mostrar algumas coisas, mas não pretendo ter ele como foco em nenhum post futuro. E Jade roda no browser também (com algum esforço), se você gostou pode usar ele como engine no lugar de templates como Handlebars ou Underscore.

Agora que você já entendeu o básico de como o Express funciona e viu que não tem nada demais no arquivo app.js vamos entrar na explicação de um conceito poderosíssimo para o Express, e que também está presente em outros frameworks web, como o Rails: middlewares. Mas no próximo post. Até breve.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.