fs/promises é a versão baseada em promises do módulo fs do Node. As funções dele (readFile, writeFile, readdir, stat, mkdir, rm, rename e as demais) retornam uma promise em vez de receber um callback, então você dá await nelas e trata erros com try/catch. Importe com require('fs/promises') ou import fs from 'fs/promises'.
Ele imprime [ 'write docs', 'ship it' ]: cada await espera uma operação antes de a próxima linha rodar, então o append sempre acontece depois da primeira escrita. Em um ES module o await funciona no nível superior; em CommonJS, coloque o código dentro de uma função async.
As funções que você mais vai usar
Os nomes são os mesmos da API de callbacks, sem o callback. As páginas de readFile e writeFile cobrem essas duas em detalhe.
| Função | Faz | Resolve com |
|---|---|---|
readFile(path, 'utf8') | Lê um arquivo inteiro | O conteúdo |
writeFile(path, data) | Cria ou substitui um arquivo | undefined |
appendFile(path, data) | Adiciona no final | undefined |
readdir(path) | Lista uma pasta | Um array de nomes |
stat(path) | Tamanho, datas, arquivo ou pasta | Um objeto Stats |
mkdir(path, { recursive: true }) | Cria pastas | A primeira pasta criada, ou undefined |
rename(from, to) | Move ou renomeia | undefined |
rm(path, { recursive: true, force: true }) | Apaga um arquivo ou pasta | undefined |
Listar uma pasta com readdir e stat
readdir retorna só os nomes. Chame stat em cada um para pegar o tamanho ou diferenciar arquivos de pastas, ou passe { withFileTypes: true } para receber entradas que já sabem o próprio tipo.
O loop imprime README.md 6 bytes, notes.txt 3 bytes e src folder. stat rejeita com ENOENT para um caminho que não existe, o que faz dele uma verificação rápida de existência quando você também quer os detalhes.
Renomear, mover e apagar
rename tanto renomeia quanto move um arquivo (dentro do mesmo disco). rm apaga; ele precisa de recursive: true para uma pasta com conteúdo, e de force: true para não falhar quando o caminho já não existe.
O arquivo sai de drafts para published com o nome novo, e depois do rm a pasta blog lista só published. Tire o force: true do segundo rm e ele rejeita com ENOENT.
Tratar erros com try/catch
Uma operação que falha rejeita a promise. Com await, essa rejeição vira uma exceção que você captura como qualquer outra. O code do erro diz o que deu errado.
A primeira chamada não encontra arquivo e retorna []; a segunda imprime Ada. Códigos para conhecer: ENOENT (arquivo não existe), EEXIST (já existe), EISDIR (uma pasta onde se esperava um arquivo), ENOTEMPTY (a pasta ainda tem conteúdo) e EACCES (sem permissão).
Uma promise em que você não dá await nem .catch() vira uma unhandled rejection quando falha, e o Node termina com erro. Dê await em toda chamada, ou retorne a promise para quem chamou e vai dar await.
Ler vários arquivos de uma vez com Promise.all
Um loop for com await lê os arquivos um depois do outro. Quando as leituras não dependem umas das outras, inicie todas e espere uma vez com Promise.all: o disco trabalha nelas ao mesmo tempo.
Promise.all mantém a ordem do array de entrada, então o resultado é [ 'alpha', 'beta', 'gamma' ] independentemente de qual leitura terminou primeiro, e o tamanho juntado é 14. Troque 'b.txt' no array por 'x.txt' e o await inteiro rejeita com ENOENT.
Se você precisa de todos os resultados mesmo quando algumas leituras falham, use Promise.allSettled; a página de promises cobre os dois.
Erros comuns
Confundir os dois módulos. require('fs').readFile quer um callback; require('fs/promises').readFile retorna uma promise. Chamar a versão de callback sem callback lança The "cb" argument must be of type function.
Esquecer o await. const text = fs.readFile(...) é uma promise pendente, e text.split lança text.split is not a function.
Escritas em paralelo no mesmo arquivo. Promise.all com várias escritas no mesmo caminho pode deixar o conteúdo misturado. Rode essas em sequência.
Perguntas frequentes
Qual a diferença entre fs.promises e fs/promises?
Nenhuma. require('fs').promises e require('fs/promises') são o mesmo objeto. O caminho fs/promises (ou node:fs/promises) é o jeito comum de importar hoje.
Existe existsSync no fs/promises?
Não. A API com promises tem access, que rejeita quando o caminho não existe, mas o padrão usual é chamar a operação de que você precisa e tratar ENOENT. O existsSync do módulo fs comum continua funcionando para verificações rápidas.
O fs/promises é mais lento que a API de callbacks?
Um pouco, pelo custo das promises, mas é desprezível perto do tempo de disco. Escolha pela legibilidade; a API com promises é a que a maior parte do código novo usa.
Como transformo uma função fs de callback em promise?
Raramente é preciso, já que o fs/promises cobre as funções de arquivo. Para outras APIs de callback, util.promisify(fn) devolve uma versão que retorna promise.