🌐 Detecting your location…

Como corrigir o erro MODULE_NOT_FOUND após implantar o Node.js na produção

⏱️7 min read  ·  1,376 words

Error: Cannot find module './Utils/logger'que funciona perfeitamente na sua máquina e falha no momento em que é implantado. Essa classe de bug tem um pequeno número de causas e quase todas se resumem a uma diferença entre o ambiente de desenvolvimento e o servidor.

Causa 1: Sensibilidade a maiúsculas e minúsculas (a mais comum)

macOS e Windows usam sistemas de arquivos que não diferenciam maiúsculas de minúsculas por padrão. Linux não. Entãorequire('./Utils/logger') resolve um arquivo chamadoutils/logger.js no seu laptop e falha no servidor.

// File on disk: src/utils/logger.js

const logger = require('./Utils/logger');   // works on macOS, fails on Linux
const logger = require('./utils/logger');   // ✅ correct everywhere

Encontre-os antes de implantar, verificando o que o Git realmente gravou, o que é oficial, independentemente do seu sistema de arquivos local.

# List tracked paths and eyeball the casing
git ls-files | grep -i utils

# Catch a rename that Git ignored because only the case changed
git config core.ignorecase false
git status

Se o Git registrou o caso errado, force a renomeação por meio de um nome intermediário.

git mv src/Utils src/utils-tmp
git mv src/utils-tmp src/utils
git commit -m "fix: correct directory casing for case-sensitive filesystems"

A prevenção confiável é um trabalho de CI executado no Linux. Ele detecta isso em cada solicitação pull, e não na implantação.

Causa 2: o pacote está em devDependencies

As instalações de produção ignoram as dependências de desenvolvimento, portanto, qualquer coisa importada pelo código de tempo de execução deve ser uma dependência regular.

npm ci --omit=dev        # devDependencies are not installed
{
  "dependencies": {
    "express": "^5.0.0"
  },
  "devDependencies": {
    "dotenv": "^17.0.0"     // ❌ but required at runtime in server.js
  }
}
# Move it
npm uninstall dotenv
npm install dotenv

Para encontrar todos os casos de uma vez, instale as dependências de produção em um diretório limpo e inicie o aplicativo.

rm -rf node_modules
npm ci --omit=dev
node dist/server.js

Causa 3: node_modules copiado para o Docker

Copiando um construído localmentenode_modules em uma imagem quebra módulos nativos, porque os binários compilados para macOS ou para sua arquitetura não serão carregados na plataforma do contêiner.

# .dockerignore — essential
node_modules
npm-debug.log
.git
dist
.env
# Dockerfile — install inside the image
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]

Copiandopackage*.json antes do resto da fonte é uma escolha deliberada de cache de camada: as dependências só são reinstaladas quando o manifesto muda, não em cada edição da fonte.

Causa 4: Módulos nativos criados para a plataforma errada

Pacotes com componentes compilados —bcrypt, sharp, canvas, drivers de banco de dados — produzem binários específicos da plataforma.

Error: Cannot find module '.../node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node'

Recrie-os na plataforma de destino ou instale-os dentro do contêiner conforme acima.

npm rebuild bcrypt --build-from-source

# Alpine images need build tools for native compilation
RUN apk add --no-cache python3 make g++

Onde existe uma alternativa JavaScript puro —bcryptjs em vez debcrypt, por exemplo — usá-lo remove toda essa categoria de problema de implantação.

Causa 5: Extensões de arquivo ausentes no ESM

Os módulos ES exigem a extensão nas importações relativas. CommonJS não. A migração entre eles revela isso imediatamente.

// package.json has "type": "module"
import { logger } from './utils/logger';        // ❌ ERR_MODULE_NOT_FOUND
import { logger } from './utils/logger.js';     // ✅

Na compilação do TypeScript para ESM, o especificador de importação deve fazer referência aosaída arquivo, então você escreve.js mesmo que a fonte seja.ts.

import { logger } from './utils/logger.js';   // correct — refers to compiled output

Causa 6: Aliases de caminho TypeScript não resolvidos em tempo de execução

Aliases emtsconfig.json são uma conveniência em tempo de compilação. O compilador não os reescreve, portanto o JavaScript emitido ainda contém@/utils/logger, que o Node não pode resolver.

{
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"] }
  }
}
// Compiles fine, fails at runtime:
// Error: Cannot find module '@/utils/logger'
import { logger } from '@/utils/logger';

Reescreva os caminhos após a compilação ou registre um resolvedor de tempo de execução.

npm install -D tsc-alias

# package.json
"build": "tsc && tsc-alias"

O empacotamento com esbuild, tsup ou similar também resolve aliases durante a construção, e é por isso que as implantações empacotadas raramente atingem isso.

Causa 7: A saída do build não foi implantada

A .gitignore or .dockerignore entrada paradist está correto – mas a compilação deve ser executada no servidor ou no CI. Confirme o que realmente foi enviado.

# Inspect the running container
docker exec -it <container> ls -la /app/dist
docker exec -it <container> ls -la /app/node_modules | head

Diagnosticando Sistematicamente

# 1. Which exact path is Node looking for?
node dist/server.js
# Read the full error — it prints the resolved path it tried.

# 2. Does that path exist on the server?
ls -la /app/dist/utils/

# 3. Is the package installed?
ls /app/node_modules | grep package-name
npm ls package-name

# 4. Trace resolution in detail
NODE_DEBUG=module node dist/server.js 2>&1 | head -50

# 5. Confirm the Node version matches your local one
node --version

NODE_DEBUG=module imprime todas as verificações de nó de diretório, o que geralmente torna o problema óbvio em poucas linhas.

Prevenção

  • Execute CI no Linux para que problemas de distinção entre maiúsculas e minúsculas falhem antes da mesclagem
  • Teste comnpm ci --omit=dev em CI para capturar dependências perdidas
  • Sempre.dockerignore seunode_modules
  • Confirme o arquivo de bloqueio e usenpm ci, nuncanpm install, em compilações
  • Fixe a versão principal do Node no Dockerfile e emengines
  • Prefira pacotes JavaScript puro onde um módulo nativo não é necessário

Perguntas Frequentes

P: Por que funciona localmente, mas não no Docker?
R: Diferença entre maiúsculas e minúsculas no sistema de arquivos, plataforma diferente para binários nativos e um conjunto de dependências diferente se você instalar com dependências de desenvolvimento localmente. Todos os três são eliminados pela instalação dentro da imagem.

P: Devo confirmar node_modules?
R: Não. Confirme o arquivo de bloqueio e instale durante a compilação. As dependências comprometidas são interrompidas nas mudanças da plataforma e sobrecarregam gravemente o repositório.

P: npm ci ou npm install em produção?
A: npm ci. Ele instala exatamente o que o arquivo de bloqueio especifica e falha se o manifesto e o arquivo de bloqueio discordarem, que é o que você deseja em uma compilação.

P: Como encontro incompatibilidades de maiúsculas e minúsculas em uma base de código grande?
R: Crie no Linux em CI. Esse é o único método confiável — ferramentas locais em um sistema de arquivos que não diferencia maiúsculas de minúsculas não conseguem ver o problema.

P: O módulo está em node_modules, mas ainda não foi encontrado. Por que?
R: Geralmente um conflito de dependência aninhada, um link simbólico quebrado de uma configuração de espaço de trabalho ou um pacote cujoexports campo não expõe o subcaminho que você está importando. Verifique o pacoteexports mapa em seupackage.json.

Conclusão

Somente produçãoMODULE_NOT_FOUNDerros vêm de diferenças ambientais. Verifique-os em ordem:distinção entre maiúsculas e minúsculas no Linux, importações de tempo de execução em devDependencies, um node_modules construído localmente copiado na imagem, módulos nativos compilados para a plataforma errada, faltando.js extensões no ESM e aliases de caminho TypeScript não resolvidos. A correção estrutural para quase todos eles é a mesma – construir e instalar dentro do ambiente de destino, executar CI no Linux e usarnpm ci com um arquivo de bloqueio confirmado.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা