Express.js é o servidor HTTP e plataforma de middleware mais usado para Node.js. Vamos dar uma olhada prática no que isso traz para a mesa.

Tratamento de solicitações com Express.js

O tratamento de solicitações pela Internet é uma das tarefas mais frequentes e comuns no desenvolvimento de software. Um servidor HTTP como Express.js permite definir onde as solicitações chegam, como elas são analisadas e como uma resposta é formulada. Sua enorme e duradoura popularidade é uma prova de quão eficazmente o Express.js lida com essas tarefas.

Quando você inicia um servidor HTTP em uma máquina (digamos, uma máquina virtual na nuvem), a primeira coisa que ele precisa saber é em qual porta ele irá “escutar”. As portas fazem parte do Transmission Control Protocol (TCP) que é executado abaixo do HTTP. As portas permitem que muitos serviços diferentes sejam executados na mesma máquina, cada um vinculado a seu próprio número exclusivo.

Por exemplo, para escutar na porta 3000 usando Express, faríamos o seguinte:


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

app.listen(3000, () => {
  console.log(`Server listening on port ${port}`);
});

Por si só, esta chamada não faz muito. Requer o módulo Express, que utiliza para criar um app objeto. Em seguida, ele usa o app.listen() função para escutar na porta 3000 e registrar uma mensagem quando terminar.

Também precisamos de um endpoint, que é um local específico onde as solicitações são tratadas. Para isso, precisamos adicionar um manipulador, assim:


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

app.get('/', (req, res) => {
  res.send('Hello, InfoWorld!');
});

app.listen(3000, () => {
  console.log(`Server listening on port 3000`);
});

O app.get() função corresponde ao HTTP GET método. Sempre que um GET solicitação chega ao caminho definido – neste caso, o caminho raiz em /—a função de retorno de chamada definida é executada.

Dentro da função de retorno de chamada, recebemos dois objetos, req e resrepresentando a solicitação e a resposta do aplicativo. (Usar esses nomes para os argumentos é convencional, mas não obrigatório.) Eles nos fornecem tudo o que precisamos para entender o que a solicitação contém e formular uma resposta para enviar de volta.

Neste caso, usamos res.send() para disparar uma resposta de string simples.

Para executar este servidor simples, precisaremos do Node e do NPM instalados. (Se você ainda não possui esses pacotes, você pode instalá-los usando a ferramenta NVM.) Depois que o Node estiver instalado, podemos criar um novo arquivo chamado server.jscoloque a listagem de arquivos acima nele, instale o Express.js e execute-o assim:


$ npm add express
added 65 packages, and audited 66 packages in 2s

13 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ node server.js 
Server listening on port 3000

Agora, se você visitar localhost:3000 no seu navegador, você verá uma saudação.

Expressar como middleware

Embora os endpoints nos forneçam tudo o que precisamos para atender às solicitações, também há muitas ocasiões em que precisamos executar lógica neles. Podemos fazer isso de forma geral, executando a mesma lógica em todas as solicitações ou apenas em uma parte delas. Um exemplo perene é a segurança, que requer a filtragem de muitas solicitações.

Se quiséssemos registrar todas as solicitações que chegam ao nosso servidor, faríamos o seguinte:


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

function logger(req, res, next) {
  console.error(`Incoming request: ${req.method} ${req.url}`);
  next();
}

app.use(logger);

app.get('/', (req, res) => {
  res.send('Hello, InfoWorld!');
});


app.listen(3000, () => {
  console.log(`Server listening on port 3000`);
});

Este exemplo é igual ao anterior, exceto que adicionamos uma nova função chamada logger e passou para o mecanismo do servidor com app.use(). Assim como um endpoint, a função de middleware recebe um objeto de solicitação e resposta, mas também aceita um terceiro parâmetro que nomeamos next. Chamando o next A função informa ao servidor para continuar com a cadeia de middleware e, em seguida, para qualquer endpoint da solicitação.

Endpoints expressos com parâmetros

Outra coisa que todo servidor precisa fazer é lidar com parâmetros nas solicitações. Existem alguns tipos de parâmetros. Um é um parâmetro de caminhovisto aqui em um endpoint de eco simples:


app.get('/echo/:msg', (req, res) => {
    const message = req.params.msg;
    res.send(`Echoing ${message}`);
});

Se você visitar esta rota em localhost:3000/echo/test123você receberá sua mensagem de volta para você. O parâmetro de caminho chamado :msg é identificado com a variável dois pontos e depois recuperado do req.params.msg campo.

Outro tipo de parâmetro é um parâmetro de consulta (também conhecido como pesquisa), que é definido no caminho após um caractere de ponto de interrogação (?). Lidamos com consultas como esta:


app.get('/search', (req, res) => {
  const query = req.query.q;
  console.log("Searching for: " + query);
})

Este URL localhost:3000/search?q=search term fará com que o endpoint seja ativado. A string “termo de pesquisa” será então registrada no console.

Os parâmetros de consulta são divididos em pares chave/valor. Em nosso exemplo, usamos o q chave, que é a abreviação de “consulta”.

Servindo arquivos e formulários estáticos com Express

O Express também facilita a extração do corpo de uma solicitação. Isso é enviado por um envio de formulário no navegador. Para configurar um formulário, podemos usar o suporte do Express para servir arquivos estáticos. Basta adicionar a seguinte linha ao server.jslogo após as importações:


app.use(express.static('public'));

Agora você pode criar um /public diretório e adicione um form.html arquivo:





    Simple Form


    

Simple Form





Também podemos adicionar um novo endpoint em server.js para lidar com o envio do formulário:


app.post('/submit', (req, res) => {
    const formData = req.body;
    console.log(formData); 
    res.send('Form submitted successfully!');
})

Agora, se um pedido chegar /submitserá tratado por esta função, que captura o formulário usando req.body() e registra-o no console. Podemos usar o formulário com o botão enviar ou simulá-lo com uma solicitação CURL, assim:


curl -X POST -d "name=John+Doe&[email protected]" http://localhost:3000/submit

Então o servidor irá registrá-lo:


{
  name: 'John Doe',
  email: '[email protected]'
}

Isso lhe dá tudo para lidar com envios de formulários ou até mesmo solicitações AJAX semelhantes a eles.

Módulos expressos

Usando endpoints e middleware, você pode cobrir uma ampla gama de necessidades que surgem na construção de aplicativos da web. Não demora muito para que até mesmo um aplicativo modesto se torne complicado e exija algum tipo de organização. Uma etapa que você pode realizar é usar módulos JavaScript para extrair suas rotas e middleware em seus próprios arquivos relacionados.

No Express, usamos o Router object para definir endpoints em arquivos secundários, então os importamos para o arquivo principal. Por exemplo, se quiséssemos mover nossos pontos finais de eco para fora do server.js e em seu próprio módulo, poderíamos defini-los em seus próprios echo.js arquivo:


// echo.js
const express = require('express');

const router = express.Router();

router.get('/echo/:msg', (req, res) => {
    const message = req.params.msg;
    res.send(`Module is echoing ${message}`);
});

module.exports = router;

Este arquivo expõe o mesmo endpoint de eco de antes, mas usa o express.Router() objeto de fazê-lo de uma forma reutilizável. Definimos o endpoint no objeto roteador e depois o retornamos como a exportação do módulo JavaScript com module.exports = router;.

Quando outro arquivo importa isso, ele pode adicioná-lo às suas próprias rotas, como em nosso arquivo do servidor:


// server.js
// ... The rest is the same
app.use(logger);

const echoRouter = require('./echo');
app.use(echoRouter);
//... The rest is the same

Aqui, importamos e utilizamos o roteador echo com app.use(). Dessa forma, nossas rotas definidas externamente são usadas como um plugin de middleware personalizado. Isso cria uma plataforma altamente extensível, e é bom que o mesmo conceito e sintaxe sejam usados ​​para nossas próprias rotas e extensões, bem como para plug-ins de terceiros.

Conclusão

Express.js é uma opção popular para desenvolvedores que precisam de um servidor HTTP e é fácil entender por quê. É incrivelmente útil. A baixa sobrecarga e a flexibilidade do Express brilham em trabalhos menores e em tarefas rápidas. À medida que um aplicativo cresce, o Express espera que o desenvolvedor faça mais trabalho para manter todas as partes organizadas. Outras estruturas mais estruturadas, como Next.js, fazem mais organização padrão para você.

Express.js também roda em Node, o que representa um desafio para a verdadeira simultaneidade, mas você precisa de um tráfego significativo para realmente sentir suas limitações de desempenho. Resumindo, Express.js é uma plataforma altamente capaz e madura que pode lidar com quase todos os requisitos que você desejar.

Continuaremos explorando o Express.js em meu próximo artigo, dando uma olhada em recursos mais avançados, como modelos de visualização.