Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA

Aula 5 de 6

Arquivos no Node.js: fs, path e tratamento de erros

Leia e grave JSON com node:fs/promises, monte caminhos portáveis com node:path e diferencie arquivo ausente de conteúdo inválido.

46 minutos · leitura + prática · nível iniciante

Ao terminar esta aula, você vai conseguir

  • Usar as APIs assíncronas de leitura e escrita
  • Resolver caminhos a partir do próprio módulo
  • Tratar falhas conhecidas sem esconder erros inesperados
Uma requisição sai do navegador, passa pelo Node.js e volta como JSON.

Persistir significa manter dados além da execução atual. Para este primeiro projeto, um arquivo JSON será nossa persistência. Node.js fornece node:fs para interagir com o sistema de arquivos e node:path para montar caminhos sem assumir separadores específicos do sistema operacional.

Pense num arquivo como um caderno compartilhado pelo programa: antes de anotar, você abre na página certa; depois registra e fecha. A comparação ajuda a lembrar que leitura e escrita podem demorar. O limite é que um arquivo não é banco de dados: duas escritas simultâneas, volume grande e múltiplos processos exigem coordenação que este exemplo não oferece.

Resolva o caminho a partir do módulo

O diretório atual pode mudar conforme o lugar de onde o comando foi chamado. Em ES Modules, derive o caminho do próprio arquivo:

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

const arquivoAtual = fileURLToPath(import.meta.url);
const diretorioAtual = path.dirname(arquivoAtual);
const caminhoDados = path.join(diretorioAtual, '..', 'dados', 'tarefas.json');

import.meta.url identifica o módulo como URL; fileURLToPath converte essa URL em caminho do sistema; path.dirname encontra a pasta; path.join combina os segmentos. Isso é mais explícito e portável que escrever barras manualmente.

Crie dados/tarefas.json com uma lista inicial:

json
[
  { "id": 1, "titulo": "Aprender fs", "concluida": false }
]

Leia, interprete e valide

Use a variante de Promises para não bloquear a thread enquanto o sistema operacional acessa o disco:

js
import { readFile } from 'node:fs/promises';

export async function listarTarefas() {
  const texto = await readFile(caminhoDados, 'utf8');
  const dados = JSON.parse(texto);

  if (!Array.isArray(dados)) {
    throw new Error('O arquivo de tarefas precisa conter um array');
  }

  return dados;
}

readFile entrega texto porque passamos a codificação utf8. JSON.parse transforma esse texto em valor JavaScript e pode lançar erro se a sintaxe estiver quebrada. Depois, a validação confirma a forma mínima esperada. JSON válido não garante dados adequados ao domínio.

Grave sem perder a serialização

Para adicionar uma tarefa, leia o estado atual, crie um novo array e grave:

js
import { writeFile } from 'node:fs/promises';

export async function adicionarTarefa(titulo) {
  const tarefas = await listarTarefas();
  const nova = { id: Date.now(), titulo: titulo.trim(), concluida: false };
  const atualizadas = [...tarefas, nova];

  await writeFile(caminhoDados, JSON.stringify(atualizadas, null, 2), 'utf8');
  return nova;
}

O terceiro argumento do JSON.stringify deixa o arquivo legível. Isso não torna a escrita transacional: uma aplicação real pode preferir banco de dados ou uma estratégia de arquivo temporário e renomeação.

Crie a pasta dados conscientemente antes da primeira gravação. writeFile pode criar o arquivo, mas não cria todos os diretórios ausentes do caminho. Também decida quem é responsável pelo valor inicial: o projeto pode versionar um [] ou o repositório pode tratar apenas ENOENT como primeira execução e criar a coleção. Registrar essa decisão evita que cada chamada invente um comportamento.

Depois de gravar, leia novamente e rode a validação. Essa verificação custa mais e não é necessária em toda operação real, mas é uma microprática útil nesta aula: ela comprova que serialização, caminho e codificação concordam entre si.

Ao relatar uma falha, acrescente contexto que ajude sem expor conteúdo sensível: qual operação falhou e qual arquivo lógico estava envolvido. Preserve a causa original para investigação. Uma mensagem como “não foi possível carregar tarefas” orienta melhor que “deu erro”, mas não precisa imprimir todo o conteúdo do arquivo nem dados pessoais no log.

O laboratório trabalha apenas com validação e serialização em memória. O sandbox do navegador não acessa seu disco, portanto a leitura e a gravação devem ser praticadas no projeto Node. Quebre o id de propósito e confirme a mensagem antes de restaurar o valor.

Erro comum: capturar tudo e devolver lista vazia

Um catch que retorna [] para qualquer falha esconde JSON corrompido e erro de permissão como se fossem “nenhuma tarefa”. Trate somente situações previstas, como um arquivo ainda inexistente identificado por erro.code === 'ENOENT', e propague o restante. Registrar contexto também ajuda a investigar a causa.

Na missão, rode a adição duas vezes e abra o arquivo. A conclusão exige JSON válido, três tarefas preservadas e nenhuma sobrescrita acidental do histórico. Na última aula, vamos ligar módulos, HTTP, validação e persistência numa API.

Laboratório ao vivo

Validação antes da persistência

O sandbox não acessa seu disco. Edite os dados e execute para validar a estrutura que seria serializada antes de uma gravação feita pelo Node.js.

Pronto para testar
Resultado

Pare e pense

Por que usar node:path em vez de concatenar barras manualmente?

Escolha uma resposta
Missão da aula

Faça sem copiar

Crie dados/tarefas.json, leia-o com fs/promises e grave uma nova tarefa. Rode duas vezes e confirme que o JSON continua válido e contém os itens anteriores.

Fontes para consultar

Terminou a missão?

Marque apenas quando você conseguir explicar o conceito e concluir o desafio. O progresso fica salvo somente neste navegador.

Próxima: Projeto Node.js: API de tarefas do começo ao fim →