Menu

module.exports vs exports no Node.js, com exemplos

module.exports é o valor que um arquivo CommonJS entrega para quem chama require() nele. exports é um atalho que aponta para o mesmo objeto, até você reatribuí-lo.

Esta página tem editores executáveis. Edite, execute e veja a saída na hora.

module.exports é o valor que um arquivo Node.js entrega para qualquer outro arquivo que o carregue com require(). Ele começa como um objeto vazio: adicione propriedades para exportar várias coisas, ou atribua um valor novo (uma função, uma classe) para exportar uma coisa só. exports é uma variável de atalho que aponta para o mesmo objeto, então exports.add = ... funciona, mas exports = ... quebra a ligação e não exporta nada.

Ele imprimiu 5 20 3.14159, depois [ 'add', 'multiply', 'PI' ]. As três linhas de math.js adicionaram propriedades ao mesmo objeto, seja passando por module.exports ou por exports, e require('./math') retornou esse objeto.

Exportar uma função, uma classe ou um objeto

Quando um módulo tem uma coisa principal, atribua direto a module.exports. Quem chama recebe a própria função, não um objeto em volta dela:

Ele imprimiu Hello, Ada e Grace (user). Se você vir TypeError: greet is not a function, quem chamou usou o formato errado: escreva const { greet } = require(...) para uma propriedade de objeto e const greet = require(...) para um valor atribuído.

module.exports vs exports: a armadilha da reatribuição

Antes de o seu arquivo rodar, o Node.js faz exports = module.exports. Os dois nomes apontam para um único objeto, e require retorna module.exports. Atribuir um valor novo a exports só muda a variável local:

broken.js exportou {}, porque exports agora aponta para um objeto novo que ninguém retorna. working.js exportou { version: 2 }.

Você escreveExportado?
module.exports.x = 1Sim
exports.x = 1Sim, mesmo objeto
module.exports = { x: 1 }Sim, substitui o objeto
exports = { x: 1 }Não, o valor se perde

A regra mais simples: escreva sempre module.exports. Funciona em todos os casos, e o código diz o que faz.

require retorna o mesmo objeto em cache

O Node.js roda o código de um módulo uma vez, na primeira vez que ele é chamado com require, e guarda module.exports em cache. Todo require seguinte do mesmo arquivo retorna esse mesmo objeto, então o estado dentro de um módulo é compartilhado por todos os arquivos que o carregam:

counter.js is running apareceu uma vez, b.next() retornou 3, e a === b é true, porque a chave do cache é o caminho resolvido do arquivo, e ./counter e ./counter.js resolvem para o mesmo arquivo. É por isso que uma conexão com banco de dados ou um objeto de configuração criado em um módulo é compartilhado pelo app inteiro.

module.exports e ES modules

Os ES modules usam export e import. As formas se correspondem assim:

// CommonJS                              // ES modules
module.exports = greet;                  export default greet;
module.exports.add = add;                export { add };
const greet = require('./greet');        import greet from './greet.js';
const { add } = require('./math');       import { add } from './math.js';

Um ES module pode fazer import de um arquivo CommonJS: o module.exports dele chega como o import default. Desde o Node.js 22.12, require() também consegue carregar um ES module que não tenha await no topo. Para saber quando escolher cada um, veja CommonJS vs ES modules.

Erros comuns

Misturar exports.x com module.exports = ... no mesmo arquivo. A atribuição substitui o objeto, então todo exports.x escrito antes dela se perde. Escolha um estilo por arquivo.

Exportar um valor que muda depois. module.exports = { count } copia o número no momento do carregamento; quando o módulo atualiza count, quem chamou continua vendo o valor antigo. Exporte uma função que o retorne, como counter.js faz acima.

Esquecer o ./. require('math') procura um pacote em node_modules, não o math.js ao lado do seu arquivo. Arquivos locais sempre começam com ./ ou ../.

Perguntas frequentes

Posso usar module.exports e export no mesmo arquivo?

Não. Um arquivo é CommonJS (require, module.exports) ou ES module (import, export). O Node.js decide pela extensão (.cjs ou .mjs) ou pelo "type" no package.json.

Por que o require retorna um objeto vazio?

Normalmente o módulo fez exports = ... em vez de module.exports = ..., ou dois módulos se importam um ao outro. Em um require circular, um deles recebe o module.exports do outro antes de esse arquivo terminar de rodar.

Preciso da extensão .js no require?

Não. require('./math') tenta ./math, depois ./math.js, ./math.json e ./math.node, depois uma pasta ./math com um index.js. Em um import de ES module a extensão é obrigatória.

Como recarregar um módulo que o require já colocou em cache?

Apague a entrada com delete require.cache[require.resolve('./math')] e chame o require de novo. Isso serve mais para testes e ferramentas de desenvolvimento; código de aplicação não deve depender disso.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

Começar