O Node.js inclui funções auxiliares que resolvem tarefas recorrentes de desenvolvimento, depuração e compatibilidade. O módulo Util no Node.js oferece recursos como conversão de callbacks em Promises, inspeção de objetos, formatação de mensagens, parsing de argumentos, verificação de tipos e suporte a APIs depreciadas.
Muitas dessas funções são usadas internamente por bibliotecas e ferramentas. Elas ajudam a modernizar código legado, produzir logs legíveis e criar interfaces de linha de comando sem dependências externas. Entretanto, alguns recursos são destinados a uso interno ou possuem comportamento específico que precisa ser entendido.
Neste guia, você aprenderá a usar promisify(), callbackify(), inspect(), format(), parseArgs(), types, deprecate() e outras funções com exemplos e boas práticas.
Importando o módulo Util
const util = require('node:util');Em ES Modules:
import util from 'node:util';A documentação oficial do módulo Util lista todas as APIs e seus níveis de estabilidade. Para conceitos assíncronos, veja Event Loop no Node.js. O guia da MDN sobre Promise complementa a base da linguagem.
Convertendo callbacks com promisify()
APIs antigas do Node.js frequentemente recebem um callback no formato (error, result). promisify() cria uma versão compatível com async e await:
const { promisify } = require('node:util');
const { exec } = require('node:child_process');
const execAsync = promisify(exec);
const { stdout } = await execAsync('node --version');
console.log(stdout.trim());A função original precisa seguir a convenção de callback de erro primeiro. APIs com outra assinatura podem produzir resultados incorretos.
Preservando o this
Ao promisificar um método, o contexto pode ser perdido:
const object = {
value: 42,
getValue(callback) {
callback(null, this.value);
}
};
const getValue = promisify(object.getValue);A chamada sem contexto pode falhar. Use bind:
const getValue = promisify(
object.getValue.bind(object)
);Promisify personalizado
Uma função pode definir sua própria implementação através do símbolo util.promisify.custom:
function legacyOperation(value, callback) {
setTimeout(() => callback(null, value * 2), 10);
}
legacyOperation[util.promisify.custom] = async value => {
return value * 2;
};
const modernOperation = util.promisify(legacyOperation);Isso é útil quando a API retorna vários valores ou não segue o padrão convencional.
Não promisifique APIs que já retornam Promise
Versões modernas do Node.js oferecem módulos como node:fs/promises. Prefira a API nativa baseada em Promise em vez de envolver novamente:
const fs = require('node:fs/promises');
const content = await fs.readFile('config.json', 'utf8');Veja File System no Node.js para operações de arquivos.
Convertendo Promises com callbackify()
callbackify() faz o caminho inverso:
async function loadUser(id) {
return repository.findById(id);
}
const loadUserCallback = util.callbackify(loadUser);
loadUserCallback('123', (error, user) => {
if (error) {
console.error(error);
return;
}
console.log(user);
});Esse recurso ajuda a integrar código moderno com consumidores legados. Novos módulos devem preferir Promise ou oferecer interfaces explícitas.
Inspecionando objetos com inspect()
util.inspect() produz uma representação textual adequada para depuração:
const output = util.inspect(object, {
depth: 4,
colors: process.stdout.isTTY,
compact: false
});
console.log(output);Diferentemente de JSON.stringify(), inspect suporta referências circulares, Map, Set, BigInt e vários objetos internos.
Inspect não é serialização
O resultado foi projetado para humanos e pode mudar entre versões. Não o armazene como formato de dados nem tente reconstruir objetos a partir da string.
Controlando profundidade
util.inspect(value, {
depth: 2,
maxArrayLength: 50,
maxStringLength: 500
});Limites evitam logs gigantes. Um objeto recebido do usuário pode conter arrays enormes ou estruturas profundas.
Inspect personalizado
Classes podem definir como aparecem na depuração:
const customInspect = util.inspect.custom;
class SecretValue {
constructor(value) {
this.value = value;
}
[customInspect]() {
return 'SecretValue([REDACTED])';
}
}Isso ajuda a evitar exposição acidental, mas não substitui sanitização central de logs. Outras formas de serialização ainda podem revelar o valor.
Formatando mensagens
util.format() utiliza marcadores semelhantes ao printf:
const message = util.format(
'Usuário %s realizou %d tentativas',
username,
attempts
);Marcadores comuns incluem %s, %d, %i, %f, %j, %o e %O. Verifique a documentação da versão usada.
formatWithOptions()
const message = util.formatWithOptions(
{ colors: false, depth: 3 },
'Resultado: %O',
result
);Essa função combina formatação com opções de inspect.
Parseando argumentos com parseArgs()
util.parseArgs() ajuda a criar CLIs pequenas:
const { values, positionals } = util.parseArgs({
options: {
port: { type: 'string', short: 'p' },
verbose: { type: 'boolean', short: 'v' },
output: { type: 'string' }
},
allowPositionals: true
});Converta e valide os valores:
const port = Number(values.port || 3000);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error('Porta inválida');
}Opções múltiplas
Dependendo da versão, uma opção pode aceitar múltiplos valores:
const { values } = util.parseArgs({
options: {
include: {
type: 'string',
multiple: true
}
}
});Verifique a versão mínima do Node.js, pois parseArgs() evoluiu ao longo das versões.
Tokens de argumentos
A opção de tokens permite observar como cada argumento foi interpretado. Isso é útil para mensagens de erro mais precisas e para CLIs avançadas, mas aumenta complexidade.
Verificação de tipos com util.types
const { types } = util;
console.log(types.isDate(new Date()));
console.log(types.isMap(new Map()));
console.log(types.isPromise(Promise.resolve()));Essas verificações podem ser mais confiáveis que instanceof em objetos criados em outro contexto de VM.
ArrayBuffer e Typed Arrays
types.isArrayBuffer(new ArrayBuffer(8));
types.isUint8Array(new Uint8Array(8));
types.isDataView(new DataView(new ArrayBuffer(8)));Para dados binários, veja Buffer no Node.js.
isDeepStrictEqual()
util.isDeepStrictEqual() compara estruturas profundamente:
const equal = util.isDeepStrictEqual(
{ id: 1, tags: ['a'] },
{ id: 1, tags: ['a'] }
);Em testes, prefira as asserções do módulo node:assert/strict, que produzem mensagens melhores. Em regra de negócio, avalie se uma comparação profunda é realmente necessária.
deprecate()
Bibliotecas podem marcar uma função como depreciada:
function oldFunction(value) {
return newFunction(value);
}
const deprecatedFunction = util.deprecate(
oldFunction,
'oldFunction será removida; use newFunction',
'DEP_APP_001'
);Quando chamada, a função emite um warning. Forneça alternativa e prazo de migração.
Warnings de depreciação
Não use depreciação como erro imediato. Consumidores precisam de tempo para atualizar. Em projetos internos, monitore warnings no CI e trate códigos conhecidos.
inherits()
util.inherits() existe para padrões antigos de herança baseados em construtores. Em código moderno, prefira classes:
class CustomError extends Error {
constructor(message) {
super(message);
this.name = 'CustomError';
}
}TextEncoder e TextDecoder
Alguns utilitários relacionados a codificação podem estar disponíveis globalmente ou pelo módulo, dependendo da versão. Para conversões de texto em streams, considere também node:string_decoder.
MIME e APIs novas
O módulo Util recebe recursos ao longo das versões. Sempre confirme estabilidade e compatibilidade antes de depender de uma função em biblioteca pública.
Sanitização de logs
inspect() pode revelar tokens, cookies e senhas. Antes de registrar:
function sanitize(value) {
return {
...value,
password: value.password ? '[REDACTED]' : undefined,
token: value.token ? '[REDACTED]' : undefined
};
}Uma lista de campos fixa não cobre todos os segredos. Defina políticas por contexto e evite logar objetos completos.
Desempenho
Inspeção profunda e comparação estrutural podem ser caras. Não execute inspect() em nível debug quando o nível está desativado:
if (logger.isDebugEnabled()) {
logger.debug(util.inspect(data, { depth: 3 }));
}Logs estruturados normalmente devem receber objetos sanitizados, não strings pré-formatadas.
Testando código promisificado
test('converte callback em Promise', async () => {
const legacy = (value, callback) => {
callback(null, value * 2);
};
const modern = util.promisify(legacy);
assert.equal(await modern(4), 8);
});Inclua casos de erro e verifique preservação de contexto.
Erros comuns
- Promisificar função fora do padrão: resultado e erro são interpretados incorretamente.
- Perder this: métodos falham depois da conversão.
- Usar inspect como JSON: o formato não é estável.
- Inspecionar objetos enormes: CPU e logs crescem.
- Confiar em parseArgs sem validar: todos os dados ainda são entrada externa.
- Expor segredos no inspect: dados sensíveis aparecem em logs.
- Usar API nova sem checar versão: produção pode executar runtime antigo.
Boas práticas para produção
- Prefira APIs nativas baseadas em Promise.
- Use bind ao promisificar métodos.
- Limite profundidade e tamanho de inspeção.
- Sanitize dados antes de registrar.
- Valide argumentos após parseArgs.
- Use códigos estáveis em depreciações.
- Verifique a versão mínima do Node.js.
- Evite utilitários legados em código novo.
- Meça custo de inspeções em alto volume.
- Teste casos de erro e compatibilidade.
Conclusão
O módulo Util no Node.js reúne ferramentas úteis para modernizar callbacks, depurar objetos, criar CLIs e verificar tipos. Ele reduz dependências para tarefas pequenas e oferece integração consistente com o runtime.
Esses recursos precisam ser usados com limites. Promisify depende de convenções, inspect pode revelar segredos e parseArgs não substitui validação. Ao conhecer o contrato de cada função e a versão do Node.js, você aproveita o módulo sem transformar utilidades em fonte de bugs ou overhead.




