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

Dynamic Import no Node.js: carregamento sob demanda

Atualizado em: 10 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Dynamic Import no Node.js usa a função import() para carregar módulos de forma assíncrona durante a execução. Diferente do import estático, ele pode aparecer dentro de funções, condições e fluxos que dependem de configuração ou entrada já validada.

O recurso é útil para lazy loading, plugins controlados, comandos opcionais, adaptadores de infraestrutura e interoperabilidade entre CommonJS e ESM. Porém, caminhos construídos com dados externos podem introduzir carregamento inesperado de código.

Neste guia, você aprenderá sintaxe, cache, tratamento de erros, carregamento condicional, plugins com allowlist, testes, desempenho e boas práticas para produção.

Sintaxe básica

const modulo = await import('./relatorio.js');
await modulo.gerarRelatorio();

import() retorna uma Promise resolvida com o namespace do módulo. Exports nomeados aparecem como propriedades e o export default aparece em default.

Import estático versus dinâmico

import { gerarRelatorio } from './relatorio.js';

O import estático é analisado antes da execução e deve ser preferido para dependências obrigatórias. O dinâmico é adequado quando a dependência é opcional ou só deve ser carregada em um fluxo específico.

Carregamento sob demanda

async function exportar(formato) {
  if (formato === 'csv') {
    const { gerarCsv } = await import('./exporters/csv.js');
    return gerarCsv();
  }

  if (formato === 'pdf') {
    const { gerarPdf } = await import('./exporters/pdf.js');
    return gerarPdf();
  }

  throw new Error('Formato não suportado');
}

O código do exportador só é avaliado quando o fluxo correspondente é executado.

Allowlist para caminhos

Nunca concatene diretamente entrada do usuário:

// perigoso
const plugin = await import(`./plugins/${req.query.name}.js`);

Use mapeamento fechado:

const plugins = {
  csv: './plugins/csv.js',
  pdf: './plugins/pdf.js'
};

const caminho = plugins[tipo];
if (!caminho) throw new Error('Plugin inválido');
const plugin = await import(caminho);

Usando URL relativa ao módulo

const caminho = new URL('./plugins/pdf.js', import.meta.url);
const plugin = await import(caminho);

Isso evita dependência do diretório atual do processo.

Export default

const { default: criarAdapter } = await import('./adapter.js');
const adapter = criarAdapter(config);

Quando possível, prefira exports nomeados para reduzir ambiguidade.

Import dinâmico em CommonJS

async function executar() {
  const { processar } = await import('./processador.mjs');
  return processar();
}

module.exports = { executar };

Essa é a principal ponte para consumir ESM a partir de um módulo CommonJS.

Cache de módulos

Importações repetidas do mesmo identificador normalmente reutilizam o módulo já carregado. O código de inicialização não deve ser tratado como uma função executada a cada chamada.

const a = await import('./config.js');
const b = await import('./config.js');
console.log(a === b);

Evite armazenar estado mutável global no módulo. Exporte uma fábrica para criar instâncias independentes.

Concorrência no primeiro carregamento

Duas chamadas simultâneas para o mesmo módulo compartilham o processo de carregamento. Ainda assim, a inicialização interna pode chamar recursos externos e falhar.

Separe import de inicialização:

const { criarCliente } = await import('./client.js');
const cliente = await criarCliente(config);

Tratamento de erros

try {
  const modulo = await import(caminho);
  await modulo.executar();
} catch (error) {
  logger.error({ err: error, plugin: tipo }, 'Falha ao carregar plugin');
  throw new Error('Plugin indisponível', { cause: error });
}

Não exponha caminhos internos ou stack traces ao cliente.

Timeout de inicialização

O import em si não aceita um AbortSignal para interromper avaliação síncrona do módulo. Evite módulos que executam rede no topo. Aplique timeout na função de inicialização exportada.

const { criarAdapter } = await import(caminho);
const adapter = await Promise.race([
  criarAdapter(config),
  timeout(5000)
]);

Lazy loading em CLIs

const comandos = {
  migrate: './commands/migrate.js',
  seed: './commands/seed.js',
  report: './commands/report.js'
};

Uma CLI pode carregar apenas o comando escolhido, reduzindo tempo de inicialização quando os módulos opcionais são pesados.

Adaptadores por ambiente

const adapters = {
  memory: './storage/memory.js',
  postgres: './storage/postgres.js'
};

const tipo = process.env.STORAGE;
const caminho = adapters[tipo];
if (!caminho) throw new Error('STORAGE inválido');

Valide variáveis conforme o guia Variáveis de Ambiente no Node.js.

Plugins de terceiros

Carregar pacote definido pelo usuário é execução de código. Em sistemas extensíveis, mantenha registro administrativo, assinatura, versões permitidas e isolamento operacional.

Não considere import dinâmico uma sandbox.

Desempenho

Lazy loading pode reduzir startup, mas desloca latência para a primeira requisição. Para funcionalidades críticas, pré-carregue após o servidor estar pronto ou aqueça o módulo antes de receber tráfego.

Meça com perf_hooks no Node.js.

Pré-carregamento controlado

const cache = new Map();

async function obterPlugin(nome) {
  if (!cache.has(nome)) {
    const caminho = plugins[nome];
    if (!caminho) throw new Error('Plugin inválido');
    cache.set(nome, import(caminho));
  }
  return cache.get(nome);
}

Armazenar a Promise evita disparar carregamentos duplicados.

Falha em Promise armazenada

Se o carregamento falhar, decida se o erro deve permanecer em cache ou permitir nova tentativa:

try {
  return await cache.get(nome);
} catch (error) {
  cache.delete(nome);
  throw error;
}

Retries devem ter limite e backoff. Veja Retry com Backoff no Node.js.

Testando módulos dinâmicos

Coloque a seleção de caminho em função pura:

export function resolverExporter(formato) {
  const caminho = exporters[formato];
  if (!caminho) throw new Error('Formato inválido');
  return caminho;
}

Teste allowlist, formato desconhecido, erro de carregamento, export ausente e inicialização.

Contrato do plugin

function validarPlugin(modulo) {
  if (typeof modulo.executar !== 'function') {
    throw new TypeError('Plugin sem executar()');
  }
}

Para contratos complexos, valide metadados com JSON Schema no Node.js.

Import dinâmico e ESM

Para configuração completa de módulos modernos, consulte ESM no Node.js.

Exports condicionais

Quando um pacote oferece entradas diferentes, o import dinâmico normalmente segue a condição import. Veja Conditional Exports no Node.js.

Erros comuns

  • construir caminho com entrada não validada;
  • usar import dinâmico para dependência obrigatória;
  • executar rede durante avaliação do módulo;
  • esperar recarregamento a cada chamada;
  • não validar exports;
  • ignorar latência da primeira execução;
  • tratar plugin como sandbox;
  • guardar Promise rejeitada para sempre.

Checklist para produção

  • use allowlist;
  • resolva caminhos relativos ao módulo;
  • valide contrato exportado;
  • separe carregamento e inicialização;
  • aplique timeout à inicialização;
  • meça startup e primeira chamada;
  • registre falhas sem segredos;
  • limite retries;
  • teste todos os módulos permitidos.

Conclusão

Dynamic Import no Node.js permite carregar módulos somente quando necessários e cria uma ponte prática entre CommonJS e ESM. O recurso funciona melhor quando a seleção é controlada, os módulos não possuem efeitos colaterais e a inicialização é explícita.

Use imports estáticos para dependências essenciais e reserve import() para capacidades opcionais. Com allowlist, contratos e métricas, o lazy loading pode reduzir startup sem comprometer previsibilidade.

Consulte a documentação oficial de import expressions do Node.js e a referência de import dinâmico da MDN.

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