Criando um servidor HTTP com o módulo nativo http
1. Introdução ao módulo http do Node.js
O módulo http é um dos módulos nativos mais fundamentais do Node.js. Ele permite criar servidores web sem depender de frameworks externos como Express.js, oferecendo controle total sobre o ciclo de vida das requisições HTTP. Embora frameworks adicionem produtividade, entender o módulo http puro é essencial para compreender como o Node.js funciona por baixo dos panos.
Diferenças entre http e https: Enquanto http trabalha com conexões não criptografadas na porta 80 (ou qualquer porta), o módulo https adiciona uma camada SSL/TLS usando certificados digitais, operando tipicamente na porta 443. Ambos compartilham a mesma API, mas https exige opções de segurança como key e cert.
Importando o módulo: No Node.js, você pode usar CommonJS ou ES Modules:
// CommonJS
const http = require('http');
// ES Modules (com "type": "module" no package.json)
import http from 'http';
2. Criando um servidor básico
A estrutura mínima de um servidor HTTP envolve http.createServer() e server.listen(). O callback recebe dois objetos: req (requisição) e res (resposta).
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Servidor rodando em http://localhost:${PORT}/`);
});
Ciclo de vida da requisição: O Node.js recebe a requisição, processa o callback, escreve os cabeçalhos e dados, e finaliza com res.end(). Este fluxo síncrono dentro do callback é bloqueante para aquela requisição específica.
3. Trabalhando com requisições (req)
O objeto req contém informações cruciais sobre a requisição recebida:
const server = http.createServer((req, res) => {
console.log('Método:', req.method);
console.log('URL:', req.url);
console.log('Headers:', req.headers);
res.end('Requisição recebida');
});
Lendo o corpo da requisição: Para métodos como POST ou PUT, o corpo chega em chunks assíncronos:
const server = http.createServer((req, res) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
console.log('Corpo completo:', body);
res.end('Dados recebidos');
});
});
Parsing de query strings: Você pode extrair parâmetros manualmente ou usando o módulo url:
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
const nome = parsedUrl.searchParams.get('nome');
res.end(`Olá, ${nome || 'visitante'}!`);
});
4. Trabalhando com respostas (res)
O objeto res controla o que é enviado de volta ao cliente. Você pode definir cabeçalhos de duas formas principais:
// Usando writeHead (define código e cabeçalhos de uma vez)
res.writeHead(200, { 'Content-Type': 'application/json' });
// Usando setHeader (adiciona cabeçalhos individualmente)
res.setHeader('Content-Type', 'text/html');
res.setHeader('X-Powered-By', 'Node.js');
Enviando dados: Use res.write() para dados parciais e res.end() para finalizar:
const server = http.createServer((req, res) => {
// JSON
if (req.url === '/api') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ mensagem: 'API funcionando' }));
}
// HTML
else if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Bem-vindo!</h1>');
}
// Texto puro
else {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Página não encontrada');
}
});
5. Roteamento manual sem frameworks
Sem Express, você precisa implementar roteamento manual com condicionais. Uma abordagem comum é usar switch ou if/else:
const server = http.createServer((req, res) => {
const { method, url } = req;
switch (true) {
case method === 'GET' && url === '/':
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home</h1>');
break;
case method === 'GET' && url === '/users':
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([{ id: 1, nome: 'João' }]));
break;
case method === 'POST' && url === '/users':
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const data = JSON.parse(body);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ criado: true, ...data }));
});
break;
default:
res.writeHead(404);
res.end('Rota não encontrada');
}
});
Parâmetros dinâmicos: Para rotas como /users/:id, você pode usar expressões regulares:
case method === 'GET' && url.startsWith('/users/'):
const id = url.split('/')[2];
res.end(`Usuário ID: ${id}`);
break;
6. Tratamento de erros e status codes
Responder com códigos HTTP adequados é crucial para uma API bem comportada:
const server = http.createServer((req, res) => {
try {
if (req.url === '/error') {
throw new Error('Algo deu errado!');
}
if (req.url === '/notfound') {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Recurso não encontrado');
return;
}
res.writeHead(200);
res.end('OK');
} catch (err) {
console.error('Erro no servidor:', err);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ erro: 'Erro interno do servidor' }));
}
});
Middleware caseiro para 404: Crie uma função que captura rotas não tratadas:
function notFoundHandler(res) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - Página não encontrada</h1>');
}
7. Integração com React (server-side rendering básico)
Para servir uma aplicação React em produção, você precisa servir arquivos estáticos e ter uma página HTML principal:
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
// Servir arquivos estáticos
let filePath = path.join(__dirname, 'build', req.url === '/' ? 'index.html' : req.url);
const extname = path.extname(filePath);
const mimeTypes = {
'.html': 'text/html',
'.js': 'application/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg'
};
const contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (err, data) => {
if (err) {
// Fallback para SPA - sempre servir index.html
fs.readFile(path.join(__dirname, 'build', 'index.html'), (err, data) => {
if (err) {
res.writeHead(500);
res.end('Erro interno');
return;
}
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
});
return;
}
res.writeHead(200, { 'Content-Type': contentType });
res.end(data);
});
});
Diferença desenvolvimento vs produção: Em desenvolvimento, ferramentas como Vite ou Webpack têm seus próprios servidores com hot reload. Em produção, você compila o React para arquivos estáticos na pasta build e usa um servidor Node.js (ou Nginx) para servi-los.
8. Boas práticas e próximos passos
Encerramento gracioso: Sempre trate sinais para encerrar o servidor corretamente:
process.on('SIGINT', () => {
console.log('\nEncerrando servidor...');
server.close(() => {
console.log('Servidor encerrado');
process.exit(0);
});
});
Limitações do módulo http puro: Embora poderoso, gerenciar rotas complexas, middlewares, parsing de corpo, cookies e sessões manualmente se torna trabalhoso. É aí que frameworks como Express.js entram, abstraindo essas complexidades.
Quando evoluir: Considere migrar para Express.js quando:
- Precisar de middlewares reutilizáveis (autenticação, logging, CORS)
- O número de rotas crescer significativamente
- Precisar de parsing automático de corpo (JSON, URL-encoded)
- Quiser integração mais fácil com templates ou WebSockets
O módulo http nativo é a base sobre a qual todo o ecossistema Node.js é construído. Dominá-lo proporciona uma compreensão profunda que será valiosa mesmo quando você estiver usando frameworks mais avançados.
Referências
- Documentação oficial do módulo http do Node.js — Referência completa da API do módulo http, incluindo todas as classes, métodos e eventos.
- Node.js HTTP Server Tutorial — Guia oficial da Node.js Foundation sobre a anatomia de uma transação HTTP.
- Criando um servidor HTTP do zero com Node.js — Tutorial prático no freeCodeCamp cobrindo desde o básico até roteamento e tratamento de erros.
- Node.js HTTP Module Examples — Exemplos simples e diretos para iniciantes no W3Schools.
- Servindo React App com Node.js HTTP Server — Artigo no Dev.to mostrando como servir uma aplicação React usando apenas o módulo http nativo.