Node.js I/O

Node.js File System

O módulo fs lê, escreve e monitora arquivos. Use a API de promises, manipule caminhos com segurança e utilize streams para qualquer conteúdo volumoso.

intermediate14 min readUpdated 15 de set. de 2026
config.js
js
// config.js
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";

const file = path.join(import.meta.dirname, "config.json");

const config = JSON.parse(await readFile(file, "utf8"));
config.updatedAt = new Date().toISOString();

await writeFile(file, JSON.stringify(config, null, 2));
Módulo
node:fs/promises
Caminhos
node:path
Encoding
utf8 para texto
Diretórios
mkdir, readdir, rm
Arquivos grandes
Streams
Segurança
Proteção contra path traversal

Por que importa

Por que a manipulação de arquivos é importante

Armazenamento persistente

Arquivos guardam configurações, uploads, logs e saídas geradas que devem sobreviver a um reinício.

Segurança é fundamental

Caminhos não confiáveis e leituras sem limite são duas das vulnerabilidades mais comuns no lado do servidor.

Streaming mantém a memória estável

Utilize streams para arquivos grandes em vez de carregá-los inteiros no buffer, evitando que a memória cresça conforme o tamanho do arquivo.

O panorama completo

Os três pilares do trabalho com arquivos

Leia e escreva conteúdo, gerencie caminhos e diretórios com segurança e utilize streams para arquivos grandes.

Conteúdo

Ler e escrever

Leia, escreva e anexe conteúdo a arquivos, utilizando encoding para texto.

Caminhos

Localizar

O módulo path une, resolve e normaliza caminhos entre diferentes plataformas.

Streaming

Escalar

Use streams para arquivos grandes e pipelines para processamento.

O sistema de arquivos em resumo

As APIs principais

readFile e writeFile

Lê e escreve arquivos completos como string ou buffer.

mkdir e readdir

Cria e lista diretórios.

módulo path

join, resolve, basename, extname e normalize.

stat

Verifica existência, tamanho, tipo e timestamps.

watch

Reage a mudanças em arquivos para servidores de dev e ferramentas.

Streams

createReadStream e createWriteStream para grandes volumes de dados.

Uma breve historia

Do fs com callbacks ao I/O baseado em promises

  1. 2009

    Callback fs

    O Node expõe o acesso a arquivos através de callbacks error-first.

    09
  2. 2014

    Proposta do fs.promises

    Uma API baseada em promises é projetada para o JavaScript moderno.

    14
  3. 2019

    fs/promises estável

    Métodos de promise tornam-se a forma recomendada de usar o fs.

    19
  4. 2023

    Suporte a Glob

    Padrões glob nativos removem uma dependência comum.

    23
  5. Hoje

    Promises primeiro

    O acesso assíncrono a arquivos é o padrão; APIs síncronas servem para scripts e inicialização.

    Hoje

O guia completo

Node.js File System: Tudo que voce precisa saber

Por que a manipulação de arquivos é importante

Servidores leem e escrevem arquivos constantemente: configurações, uploads, logs, caches, relatórios gerados e outputs de build. O módulo node:fs do Node.js é a interface, e a forma como você o utiliza decide se o seu servidor permanecerá responsivo e se os seus dados estarão seguros.

Três pontos são fundamentais. Use a promise API para que o event loop permaneça livre. Construa paths de forma segura para que a entrada do usuário não consiga escapar de um diretório. E utilize streams para arquivos grandes, evitando que o consumo de memória cresça proporcionalmente ao tamanho do arquivo.

Lendo e escrevendo arquivos

A API de promises é o padrão para o código da aplicação.

// files.js
import { readFile, writeFile, appendFile } from "node:fs/promises";

// text
const text = await readFile("notes.md", "utf8");
await writeFile("notes.md", text + "\nnew line\n", "utf8");
await appendFile("app.log", "event\n", "utf8");

// binary
const image = await readFile("photo.png"); // Buffer
await writeFile("copy.png", image);

Passe uma codificação como "utf8" para obter uma string; omita-a para obter um Buffer de bytes brutos. writeFile substitui o arquivo, enquanto appendFile adiciona conteúdo a ele. Nenhum dos dois cria diretórios inexistentes, portanto, chame mkdir primeiro quando necessário.

Diretórios

// dirs.js
import { mkdir, readdir, rm, rename, stat } from "node:fs/promises";

await mkdir("data/cache", { recursive: true });

const entries = await readdir("data", { withFileTypes: true });
for (const entry of entries) {
  if (entry.isDirectory()) console.log("dir", entry.name);
}

const info = await stat("data/cache");
console.log(info.size, info.mtime);

await rename("data/tmp.json", "data/final.json");
await rm("data/old", { recursive: true, force: true });

recursive: true faz com que mkdir e rm operem em árvores completas, e withFileTypes retorna entradas que você pode testar sem chamadas extras de stat. Use stat para verificar existência, tamanho e timestamps, mas esteja ciente de que ele segue symlinks; lstat não segue.

Caminhos

O módulo node:path lida com a manipulação de caminhos de forma portátil.

// paths.js
import path from "node:path";

const file = path.join("uploads", "avatars", "ada.png");
const absolute = path.resolve("uploads", "ada.png");
const name = path.basename(file);          // ada.png
const ext = path.extname(file);            // .png
const dir = path.dirname(file);            // uploads/avatars

Use path.join e path.resolve em vez de concatenação de strings, e import.meta.dirname (ou import.meta.url em versões mais antigas) para localizar arquivos relativos ao módulo atual. No Windows, path utiliza barras invertidas, e é por isso que a concatenação manual falha.

Segurança: path traversal

A vulnerabilidade de arquivos mais comum é permitir que a entrada do usuário escape do diretório pretendido.

// safe-path.js
import path from "node:path";

const BASE = "/srv/uploads";

function safePath(name) {
  const target = path.resolve(BASE, name);
  if (target !== BASE && !target.startsWith(BASE + path.sep)) {
    throw new Error("Invalid path");
  }
  return target;
}

Resolva a entrada em relação a uma base fixa e, em seguida, verifique se o resultado ainda está dentro dela. Além disso, valide os nomes dos arquivos, rejeite null bytes e nunca exponha erros brutos do sistema de arquivos para os clientes. Consulte o guia de Web Security.

Escritas atômicas

Se ocorrer uma falha durante a escrita, um leitor poderá visualizar um arquivo escrito parcialmente. Para estado e configuração, escreva em um arquivo temporário e renomeie-o para o local final.

// atomic.js
import { writeFile, rename } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const tmp = `/srv/state/.${randomUUID()}.tmp`;
await writeFile(tmp, JSON.stringify(state), "utf8");
await rename(tmp, "/srv/state/current.json");

rename é atômico no mesmo sistema de arquivos, portanto, os leitores sempre verão ou o arquivo antigo ou o novo completo.

Streaming de arquivos grandes

Para qualquer conteúdo que possa ser volumoso, utilize stream em vez de buffering.

// stream.js
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";

await pipeline(
  createReadStream("big.csv"),
  createWriteStream("copy.csv"),
);

O streaming mantém o uso de memória estável e permite que você comece o processamento antes mesmo que o arquivo inteiro seja lido. O guia de Streams aborda transforms, backpressure e object mode.

Monitorando arquivos

// watch.js
import { watch } from "node:fs";

const watcher = watch("./config", { recursive: true }, (event, filename) => {
  console.log(event, filename);
});

process.on("SIGINT", () => watcher.close());

Watchers são ideais para servidores de desenvolvimento e recarregamento de configurações. Utilize debounce para evitar rajadas de eventos, trate exclusões e renomeações, e sempre feche o watcher para que o processo possa ser encerrado.

Melhores práticas

  • Use node:fs/promises no código da aplicação.
  • Leia e escreva com uma codificação (encoding) explícita, ou espere um Buffer.
  • Construa caminhos com node:path e resolva-os em relação a uma base conhecida.
  • Proteja-se contra path traversal em qualquer nome de arquivo fornecido pelo usuário.
  • Use streams para arquivos grandes; faça buffer apenas do que você precisar por inteiro.
  • Escreva arquivos de estado atomicamente usando um arquivo temporário e renomeando-o.
  • Feche watchers e file handles para que os processos possam encerrar corretamente.

Erros comuns

  • Usar readFileSync em um request handler.
  • Concatenar entradas do usuário em caminhos (paths).
  • Assumir que um diretório existe antes de escrever.
  • Ler um arquivo enorme na memória e causar crash sob carga.
  • Expor erros brutos do sistema de arquivos e vazar caminhos.
  • Esquecer de fechar watchers ou streams.

Próximos passos

A manipulação de arquivos é uma tarefa diária no servidor. Aprofunde seus conhecimentos com Streams, entenda o agendamento no guia do Event Loop e torne seus caminhos mais seguros com o guia de Web Security.

Lendo um arquivo

Use a API de promises em servidores para que o event loop permaneça livre. Chamadas síncronas bloqueiam todas as outras requisições.

Preferível
import { readFile } from "node:fs/promises";

const text = await readFile("data.json", "utf8");
Evite
import { readFileSync } from "node:fs";

// blocks the whole server
const text = readFileSync("data.json", "utf8");

Construindo um caminho

Resolva a entrada do usuário contra uma base fixa e rejeite qualquer coisa que escape dela. A concatenação de strings convida a ataques de path traversal.

Preferível
import path from "node:path";

const base = "/srv/uploads";
const target = path.resolve(base, name);

if (!target.startsWith(base + path.sep)) {
  throw new Error("Invalid path");
}
Evite
// "../../etc/passwd" escapes
// the intended directory
const target = "/srv/uploads/" + name;

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender File System?

Nosso tutorial interativo te guia por File System passo a passo — com quizzes e codigo real que voce pode executar no navegador.