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 res
representando 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.js
coloque 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/test123
você 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.js
logo 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 /submit
será 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.