Cliente HTTP em JavaScript: fazendo requisições corretamente no Node.js
O desenvolvimento web moderno é impensável sem interação com servidores remotos. Seja buscando dados de uma API REST, enviando formulários ou fazendo upload de arquivos — requisições HTTP estão no centro de qualquer integração. Se você está desenvolvendo em Node.js, tem várias ferramentas poderosas para criar um cliente HTTP. Neste artigo, vamos detalhar como fazer requisições corretamente: desde módulos nativos até bibliotecas populares, com ênfase em tratamento de erros, timeouts e legibilidade do código.
Vamos abordar três abordagens principais: o módulo nativo http/https, o moderno fetch (disponível no Node.js 18+) e a lendária biblioteca axios. Para cada abordagem, você encontrará exemplos práticos e recomendações.
1. Módulo nativo http/https: uma abordagem clássica
O Node.js inclui os módulos http e https para trabalhar com requisições de rede desde o início. Este é o método de mais baixo nível, dando a você controle total sobre o processo. No entanto, requer mais código para lidar com a resposta.
Exemplo de uma requisição GET usando o módulo https:
const https = require('https');
function getUsers() { return new Promise((resolve, reject) => { const options = { hostname: 'jsonplaceholder.typicode.com', path: '/users', method: 'GET', headers: { 'Content-Type': 'application/json' } };
const req = https.request(options, (res) => { let data = '';
// Coleta dados em partes res.on('data', (chunk) => { data += chunk; });
// Quando a resposta é totalmente recebida res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(JSON.parse(data)); } else { reject(new Error(`HTTP Error: ${res.statusCode}`)); } }); });
// Trata erros de rede req.on('error', (err) => { reject(err); });
// Timeout em caso de travamento req.setTimeout(5000, () => { req.destroy(); reject(new Error('Request timeout')); });
req.end(); });}
// UsogetUsers() .then(users => console.log('Users:', users.length)) .catch(err => console.error('Error:', err.message));Prós:
- Não é necessário instalar pacotes adicionais.
- Controle total sobre cada etapa da conexão.
Contras:
- Muito código boilerplate.
- Manipulação manual de partes e montagem do corpo da resposta.
- Trabalho inconveniente com promises sem um wrapper.
Esta abordagem é justificada se você está escrevendo uma biblioteca para operações de rede ou precisa de desempenho máximo sem dependências extras. Em outros casos, é melhor usar soluções de nível mais alto.
2. Fetch API: o padrão moderno
A partir do Node.js 18, fetch se tornou uma função nativa, tornando-se a ferramenta principal para requisições HTTP. Ela é baseada em promises e tem uma sintaxe limpa e intuitiva.
Exemplo de uma requisição POST com fetch:
async function createPost() { const url = 'https://jsonplaceholder.typicode.com/posts'; const newPost = { title: 'Cliente HTTP em JavaScript', body: 'Fazendo requisições corretamente', userId: 1 };
try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newPost) });
// Verifica o status da resposta if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const data = await response.json(); console.log('Created post ID:', data.id); return data; } catch (error) {