Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Undici no Node.js

Atualizado em: 2 de outubro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Undici é o cliente HTTP de alto desempenho que serve de base para o Fetch disponível no Node.js. A biblioteca oferece APIs de baixo e alto nível para controlar conexões, pools, pipelining, streaming, timeouts e dispatchers.

Para chamadas simples, o Fetch global costuma ser suficiente. Undici se torna útil quando a aplicação precisa ajustar pool por origem, monitorar conexões, usar pipelines, construir um cliente reutilizável ou obter desempenho previsível em grande volume.

Instalação

npm install undici

Embora o Node.js use uma versão interna para Fetch, instalar o pacote permite acessar APIs específicas e controlar a versão pelo projeto.

request

import { request } from 'undici';

const { statusCode, headers, body } = await request(
  'https://api.example.com/users/42',
  {
    method: 'GET',
    headers: {
      accept: 'application/json',
    },
  },
);

if (statusCode !== 200) {
  body.resume();
  throw new Error(`HTTP ${statusCode}`);
}

const data = await body.json();
console.log(data);

O status HTTP não gera exceção automaticamente. Verifique o código e sempre consuma ou descarte o corpo para liberar a conexão.

Client

Client representa uma conexão ou conjunto controlado para uma origem.

import { Client } from 'undici';

const client = new Client('https://api.example.com', {
  connections: 10,
  pipelining: 1,
});

const response = await client.request({
  path: '/health',
  method: 'GET',
});

console.log(response.statusCode);
await response.body.text();

Reutilize o cliente. Criar uma instância por requisição elimina os benefícios do pool.

Pool

Pool gerencia várias conexões para a mesma origem:

import { Pool } from 'undici';

const pool = new Pool('https://api.example.com', {
  connections: 20,
  pipelining: 1,
});

const { body } = await pool.request({
  path: '/items',
  method: 'GET',
});

const items = await body.json();

Dimensione connections pela concorrência, latência da dependência, quantidade de réplicas e limites externos.

Pipelining

HTTP pipelining envia várias requisições na mesma conexão sem aguardar respostas anteriores. Ele pode aumentar throughput, mas respostas seguem em ordem. Uma chamada lenta pode atrasar as seguintes.

Use valores maiores somente para operações idempotentes e serviços conhecidos. Faça benchmark com latência real e falhas.

Dispatcher

Dispatcher é a abstração central de envio. Client, Pool e Agent implementam essa interface. Funções podem receber um dispatcher sem conhecer a configuração completa.

async function fetchUser(id, { dispatcher, signal }) {
  const response = await fetch(`https://api.example.com/users/${id}`, {
    dispatcher,
    signal,
  });

  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

Agent

Um Agent cria pools por origem:

import { Agent } from 'undici';

const agent = new Agent({
  connections: 20,
  pipelining: 1,
});

Ele é útil quando a aplicação acessa diversas origens conhecidas. Ainda assim, imponha allowlist para URLs externas.

Dispatcher global

import {
  Agent,
  setGlobalDispatcher,
} from 'undici';

setGlobalDispatcher(new Agent({
  connections: 50,
  keepAliveTimeout: 10_000,
}));

Alterar globalmente afeta chamadas Fetch que usam o dispatcher padrão. Faça isso no bootstrap, documente e evite bibliotecas alterando estado global.

Timeouts

Timeouts podem envolver cabeçalhos, corpo, conexão e deadline total. Use AbortSignal para o orçamento da operação:

const signal = AbortSignal.timeout(5_000);

const response = await fetch(url, {
  dispatcher: pool,
  signal,
});

O timeout deve ser menor que o prazo do chamador. Diferencie timeout de cabeçalhos e de corpo quando precisar diagnosticar.

Headers timeout

Uma dependência pode aceitar a conexão e demorar para enviar cabeçalhos. Configure um limite coerente no client ou pool e monitore o erro específico.

Body timeout

Depois dos cabeçalhos, o corpo pode parar. Limite inatividade e tamanho. Uma resposta que envia um byte periodicamente pode evitar um timeout de inatividade, por isso também use deadline total.

Streaming de resposta

import { pipeline } from 'node:stream/promises';
import { createWriteStream } from 'node:fs';

const { body, statusCode } = await pool.request({
  path: '/exports/latest',
  method: 'GET',
});

if (statusCode !== 200) {
  body.resume();
  throw new Error(`HTTP ${statusCode}`);
}

await pipeline(body, createWriteStream('export.csv'));

Pipeline aplica backpressure e fecha recursos em erros.

Enviando stream

O corpo de uma requisição pode ser string, Buffer, iterable ou stream conforme a API. Não informe Content-Length incorreto. Para uploads grandes, use backpressure e cancelamento.

FormData

const form = new FormData();
form.set('name', 'arquivo');
form.set('file', new Blob([buffer]), 'data.bin');

const response = await fetch(url, {
  method: 'POST',
  body: form,
  dispatcher: pool,
});

Para arquivos enormes, evite acumular todo conteúdo em Buffer. Verifique suporte e estratégia de streaming da versão usada.

Interceptors

Undici oferece mecanismos para compor comportamentos como redirect, retry, cache ou logging conforme a versão da biblioteca. Interceptores devem preservar cancelamento e não repetir operações não idempotentes.

Retry

Faça retry apenas de erros transitórios e métodos seguros ou protegidos por idempotency key. Respeite Retry-After e aplique backoff com jitter.

Redirects

Redirects podem mudar host, protocolo e método. Limite a quantidade e revalide destinos para evitar SSRF e vazamento de Authorization.

Proxy

Ambientes corporativos podem exigir proxy HTTP. Use dispatcher próprio e não confie cegamente em variáveis de ambiente fornecidas por usuários. Proteja credenciais do proxy.

TLS

Configurações de conexão podem incluir CA privada, certificados de cliente e SNI. Não desative rejectUnauthorized. Centralize a configuração por origem.

DNS

O pool mantém conexões ligadas aos IPs resolvidos quando foram abertas. Se o serviço muda de endereço, sockets existentes permanecem. Defina vida útil e reciclagem adequadas.

Consumo do corpo

Um erro comum é verificar o status e lançar exceção sem consumir o corpo:

if (statusCode >= 400) {
  const errorBody = await body.text();
  throw new Error(`HTTP ${statusCode}: ${errorBody.slice(0, 500)}`);
}

Limite o tamanho lido. Respostas de erro podem ser enormes ou maliciosas.

Pool saturado

Quando todas as conexões estão ocupadas, requisições esperam. Uma fila grande aumenta latência. Combine pool com semáforo, deadline, circuit breaker e limite de carga.

Diagnóstico

Monitore:

  • conexões abertas;
  • conexões ocupadas;
  • requisições pendentes;
  • tempo na fila;
  • tempo de conexão;
  • tempo até headers;
  • tempo do corpo;
  • bytes;
  • erros por código;
  • resets e timeouts.

Diagnostics Channel

Undici publica eventos de diagnóstico que agentes de observabilidade podem assinar. Use diagnostics_channel com cuidado para não registrar headers sensíveis ou adicionar trabalho pesado.

MockAgent

Testes não precisam acessar a internet:

import {
  MockAgent,
  setGlobalDispatcher,
} from 'undici';

const mockAgent = new MockAgent();
mockAgent.disableNetConnect();

const mockPool = mockAgent.get('https://api.example.com');
mockPool.intercept({
  path: '/users/42',
  method: 'GET',
}).reply(200, { id: 42, name: 'Ana' });

setGlobalDispatcher(mockAgent);

Restaure o dispatcher após o teste e feche agentes.

Graceful shutdown

await pool.close();

close permite concluir o trabalho conforme o contrato. Em prazo excedido, use destruição forçada com consciência de que requisições serão interrompidas.

Erros comuns

  • criar Pool por requisição;
  • não consumir o corpo;
  • usar conexões ilimitadas;
  • ativar pipelining sem benchmark;
  • não definir deadline;
  • repetir POST sem idempotência;
  • seguir redirects sem validar;
  • registrar Authorization;
  • não fechar dispatcher no shutdown;
  • confundir erro de rede com status HTTP.

Fluxo recomendado

Use Fetch para casos simples e um Pool ou Agent reutilizável quando precisar de controle. Limite conexões, consuma corpos, propague AbortSignal e monitore fila e timeouts. Combine com HTTP Keep-Alive, AbortController, Circuit Breaker e Diagnostics Channel.

Consulte a documentação oficial do Undici e o repositório oficial do projeto.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita