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

Módulo Path no Node.js: Guia Prático

Atualizado em: 9 de agosto de 2026

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

Caminhos de arquivos parecem simples até a aplicação precisar funcionar em Linux, Windows, containers e ambientes de teste. Barras diferentes, diretórios relativos, extensões, nomes inesperados e entrada fornecida pelo usuário podem causar erros ou vulnerabilidades. O módulo Path no Node.js oferece funções nativas para construir, analisar, normalizar e comparar caminhos de maneira portável.

O módulo não acessa o sistema de arquivos. Ele apenas trabalha com strings que representam caminhos. Essa separação permite montar e validar localizações antes de chamar APIs como fs.readFile(), fs.writeFile() ou fs.stat().

Neste guia, você aprenderá a usar join(), resolve(), normalize(), basename(), dirname(), extname(), parse(), format(), relative(), as variantes POSIX e Windows e estratégias contra path traversal.

Importando o módulo Path

const path = require('node:path');

Em ES Modules:

import path from 'node:path';

A documentação oficial do módulo Path apresenta todas as funções e diferenças de plataforma. Para operações de arquivo, consulte File System no Node.js. A página da OWASP sobre path traversal descreve riscos de segurança relacionados.

Por que não concatenar caminhos manualmente?

Este código depende do separador e pode gerar barras duplicadas:

const file = baseDirectory + '/' + userDirectory + '/config.json';

Com path.join(), o runtime utiliza as regras da plataforma:

const file = path.join(
  baseDirectory,
  userDirectory,
  'config.json'
);

Isso melhora portabilidade, mas não valida se userDirectory tenta sair da pasta permitida. Construção correta e autorização de caminho são problemas diferentes.

path.join()

join() combina segmentos e normaliza separadores:

const result = path.join(
  'storage',
  'users',
  '123',
  'avatar.png'
);

console.log(result);

Segmentos vazios são ignorados. Se todos forem vazios, o resultado representa o diretório atual.

Normalização durante o join

const result = path.join(
  '/srv/app',
  'uploads',
  '..',
  'public',
  'image.png'
);

O segmento .. remove o diretório anterior durante a normalização. Isso é conveniente para caminhos controlados, mas perigoso quando partes vêm do usuário.

path.resolve()

resolve() produz um caminho absoluto, processando os segmentos da direita para a esquerda até encontrar uma raiz:

const absolute = path.resolve(
  'storage',
  'reports',
  'daily.csv'
);

console.log(absolute);

Quando nenhum segmento absoluto é encontrado, o diretório retornado por process.cwd() é usado como base.

Diferença entre join e resolve

path.join('/srv/app', '/uploads/file.txt');
path.resolve('/srv/app', '/uploads/file.txt');

join() combina e normaliza. resolve() considera o último segmento absoluto como nova raiz. No exemplo, o segundo resultado ignora /srv/app. Essa diferença pode causar falhas de segurança se um valor externo começar com uma raiz.

path.normalize()

const normalized = path.normalize(
  '/srv//app/uploads/../public/file.txt'
);

A função remove separadores redundantes e resolve . e ... Ela não confirma que o arquivo existe e não impede que o resultado saia de um diretório permitido.

basename()

basename() retorna o último componente:

const name = path.basename('/srv/files/report.pdf');
console.log(name); // report.pdf

Também é possível remover uma extensão específica:

const name = path.basename(
  '/srv/files/report.pdf',
  '.pdf'
);

console.log(name); // report

A comparação da extensão respeita capitalização da string. Em sistemas de arquivos case-insensitive, isso pode ser diferente do comportamento real do disco.

dirname()

const directory = path.dirname(
  '/srv/files/reports/daily.csv'
);

console.log(directory);

A função retorna apenas a parte do diretório. Ela não cria a pasta. Para isso, use fs.mkdir() com a opção recursive.

extname()

console.log(path.extname('photo.png')); // .png
console.log(path.extname('archive.tar.gz')); // .gz
console.log(path.extname('.env')); // vazio

extname() retorna a última extensão. Um arquivo oculto iniciado por ponto pode não ser tratado como extensão. Valide o formato de acordo com sua regra de negócio.

parse()

parse() decompõe um caminho:

const parsed = path.parse(
  '/srv/files/archive.tar.gz'
);

console.log(parsed);

O objeto contém root, dir, base, ext e name. Essa estrutura facilita alterar uma parte sem manipular a string inteira.

format()

const file = path.format({
  dir: '/srv/reports',
  name: 'daily',
  ext: '.csv'
});

format() monta um caminho a partir de um objeto. Quando base está presente, ele pode ter prioridade sobre name e ext, conforme a versão e as regras documentadas.

relative()

relative() calcula como chegar de um caminho a outro:

const relative = path.relative(
  '/srv/app/src',
  '/srv/app/tests/unit'
);

console.log(relative);

O resultado pode conter ... Não o utilize diretamente como autorização para acesso.

isAbsolute()

path.isAbsolute('/srv/app');
path.isAbsolute('uploads/file.png');

Detectar que um caminho é absoluto não significa que ele é seguro. Um caminho absoluto fornecido pelo cliente pode apontar para qualquer região acessível ao processo.

Separadores de diretório

console.log(path.sep);

No POSIX, o separador normalmente é /. No Windows, é \. Para dividir listas de caminhos, como a variável PATH, use path.delimiter:

const entries = process.env.PATH
  ?.split(path.delimiter)
  .filter(Boolean);

path.posix e path.win32

Você pode processar caminhos de outra plataforma independentemente do sistema atual:

const linuxPath = path.posix.join(
  '/srv',
  'app',
  'file.txt'
);

const windowsPath = path.win32.join(
  'C:\\',
  'app',
  'file.txt'
);

Isso é útil ao gerar arquivos de configuração, manifestos ou pacotes destinados a outra plataforma.

Caminhos em ES Modules

ES Modules não fornecem __dirname diretamente. Use URL:

import { fileURLToPath } from 'node:url';
import path from 'node:path';

const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);

Veja ES Modules no Node.js e URL API no Node.js para conversões corretas.

URLs não são caminhos

Não tente converter uma URL removendo manualmente file://. Caracteres escapados, espaços, drives e nomes UNC exigem regras próprias:

const filename = fileURLToPath(
  new URL('./data.json', import.meta.url)
);

Path traversal

Considere um endpoint de download:

const file = path.join(
  uploadsDirectory,
  req.params.filename
);

Se o nome for ../../config.env, a normalização pode sair da pasta de uploads. Apenas usar join() não protege o acesso.

Validando contenção no diretório

function resolveInside(base, requested) {
  const baseAbsolute = path.resolve(base);
  const target = path.resolve(baseAbsolute, requested);
  const relative = path.relative(baseAbsolute, target);

  if (
    relative.startsWith('..') ||
    path.isAbsolute(relative)
  ) {
    throw new Error('Caminho fora do diretório permitido');
  }

  return target;
}

Essa validação impede saídas lexicais simples. Ainda é necessário considerar links simbólicos e alterações concorrentes no sistema de arquivos.

Um caminho aparentemente dentro da pasta pode apontar para fora através de symlink. Em operações sensíveis, obtenha o caminho real com fs.realpath() e compare novamente com a raiz real.

const realBase = await fs.realpath(baseDirectory);
const realTarget = await fs.realpath(target);

const relative = path.relative(realBase, realTarget);

Para criação de novos arquivos, o destino pode ainda não existir. Avalie o caminho real do diretório pai, permissões e uso de flags que reduzam riscos de symlink.

Nomes de upload

Não preserve diretamente o nome enviado pelo usuário:

const safeName = `${crypto.randomUUID()}.bin`;
const destination = path.join(uploadDirectory, safeName);

Armazene o nome original apenas como metadado sanitizado. Isso evita colisões, caracteres problemáticos e caminhos incorporados.

Extensão não confirma conteúdo

Um arquivo chamado photo.jpg pode conter outro formato. Verifique assinatura binária ou use uma biblioteca adequada. Também limite tamanho e armazene uploads fora da raiz pública quando possível.

Caminhos e arquivos estáticos

Ao servir conteúdo estático, defina uma raiz fixa e deixe o framework realizar validações. Não construa respostas a partir de um caminho arbitrário recebido pela URL.

Testes multiplataforma

test('cria caminho Windows', () => {
  assert.equal(
    path.win32.join('C:\\app', 'data', 'file.json'),
    'C:\\app\\data\\file.json'
  );
});

Use path.posix e path.win32 em testes para verificar regras sem depender do sistema do CI.

Erros comuns

  • Concatenar com barra: o código deixa de ser portável.
  • Achar que normalize protege: path traversal continua possível.
  • Confiar apenas em extensão: conteúdo malicioso pode usar nome permitido.
  • Ignorar symlinks: o caminho real pode sair da raiz.
  • Confundir URL e path: caracteres escapados são interpretados incorretamente.
  • Usar cwd para arquivos do módulo: o serviço pode iniciar em outro diretório.
  • Preservar nome de upload: colisões e caracteres perigosos aparecem.

Boas práticas para produção

  • Use join e resolve em vez de concatenação.
  • Defina raízes absolutas controladas.
  • Valide que destinos permanecem dentro da raiz.
  • Considere caminhos reais e symlinks.
  • Gere nomes aleatórios para uploads.
  • Converta URLs com fileURLToPath.
  • Teste regras POSIX e Windows.
  • Valide conteúdo, tamanho e permissões.
  • Evite expor caminhos internos em erros.
  • Separe construção de caminho da autorização.

Conclusão

O módulo Path no Node.js resolve diferenças entre plataformas e oferece ferramentas claras para montar e analisar caminhos. Funções como join, resolve, parse e relative tornam o código mais legível e testável.

Segurança exige etapas adicionais. Normalização não impede path traversal, extensões não validam conteúdo e symlinks podem alterar o destino real. Com raízes controladas, validação de contenção e nomes gerados pela aplicação, caminhos podem ser tratados de forma portável e segura.

Os 10 Melhores Cursos de Programação de 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