Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Working With JSON and the Filesystem in JavaScript

Working With JSON and the Filesystem in JavaScript

~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

We've already used JSON.parse/JSON.stringify several times – time for a deeper look, including a few pitfalls relevant to persisting our budget app.

JSON.stringify() in detail

const transaction = { description: 'Rent', amount: -850, category: 'Housing' };

console.log(JSON.stringify(transaction));
// '{"description":"Rent","amount":-850,"category":"Housing"}'

// With indentation for readable files (third parameter: number of spaces):
console.log(JSON.stringify(transaction, null, 2));

The second parameter (replacer) lets you exclude or transform certain properties – useful for e.g. keeping internal, non-persisted fields out of the output:

const transaction = { description: 'Rent', amount: -850, internalCache: { irrelevant: true } };

console.log(JSON.stringify(transaction, ['description', 'amount']));
// '{"description":"Rent","amount":-850}' - internalCache excluded

What JSON CANNOT represent

JSON is a plain text data format with a limited type range – some JavaScript values get LOST or UNEXPECTEDLY converted by JSON.stringify():

  • undefined as an object property gets DROPPED ENTIRELY, in an array it becomes null.
  • function values get DROPPED ENTIRELY.
  • Date objects are automatically converted to an ISO string ('2026-01-31T00:00:00.000Z'), NOT automatically converted back when parsing - JSON.parse() returns just a string again!
  • Map and Set become {} - their actual data is ENTIRELY lost.

Achtung: For our budget app, concretely: a date should be stored as a plain string ('2026-01-31'), as we've already done since chapter 4 – NOT as a Date object, whose round trip through JSON only recovers half the information.

JSON.parse() with a reviver: transforming values while parsing

const json = '{"description":"Rent","amount":-850}';

const transaction = JSON.parse(json, (key, value) => {
  if (key === 'amount') {
    return Math.round(value); // e.g. consistently round decimals
  }
  return value;
});

console.log(transaction); // { description: 'Rent', amount: -850 }

The fs module in depth: ensuring directories exist

Before our budget app writes to a file, the target directory should exist – mkdir with the recursive option automatically creates missing intermediate directories and throws NO error if the directory already exists:

src/fileStorage.js
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';
import { FileNotFoundError, BudgetAppError } from './errors.js';

export async function loadTransactions(path) {
  try {
    const content = await readFile(path, 'utf-8');
    return JSON.parse(content);
  } catch (error) {
    if (error.code === 'ENOENT') {
      throw new FileNotFoundError(path);
    }
    throw new BudgetAppError(`Could not read ${path}: ${error.message}`);
  }
}

export async function saveTransactions(path, transactions) {
  try {
    await mkdir(dirname(path), { recursive: true }); // ensure the directory exists
    await writeFile(path, JSON.stringify(transactions, null, 2), 'utf-8');
  } catch (error) {
    throw new BudgetAppError(`Could not write ${path}: ${error.message}`);
  }
}

node:path's dirname() extracts a path's directory portion (dirname('data/transactions.json') yields 'data') – another built-in Node.js module useful for file path manipulation.

A brief mention: more robust writing

Tipp: In production-grade systems, it's common to FIRST write to a temporary file and THEN rename it (instead of overwriting the target file directly) – that way, a crash MID-WRITE leaves the old, intact file in place instead of ending up in a half-written, corrupt state. Direct writing is fine for our learning budget app, but good to know for later, real projects.