Mini Project: The Expense Tracker CLI

Junior Year, page 9 of 11. A one-minute read.

Let's build a real command-line tool that tracks your spending and saves it to a file. Make a new folder, run npm init -y, add "type": "module" to package.json, and create expenses.js:

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

const FILE = "expenses.json";

async function load() {
  try {
    return JSON.parse(await readFile(FILE, "utf8"));
  } catch {
    return []; // no file yet? start with an empty list
  }
}

async function save(expenses) {
  await writeFile(FILE, JSON.stringify(expenses, null, 2));
}

const money = (amount) => `$${amount.toFixed(2)}`;

async function main() {
  const [command, ...args] = process.argv.slice(2);
  const expenses = await load();

  switch (command) {
    case "add": {
      const amount = Number(args[0]);
      const description = args.slice(1).join(" ");
      if (Number.isNaN(amount) || amount <= 0 || !description) {
        console.log("Usage: node expenses.js add <amount> <description>");
        return;
      }
      expenses.push({ amount, description, date: new Date().toISOString().slice(0, 10) });
      await save(expenses);
      console.log(`✅ Added ${money(amount)} for "${description}"`);
      break;
    }

    case "list": {
      if (expenses.length === 0) {
        console.log("No expenses yet. Your wallet thanks you. 💰");
        return;
      }
      expenses.forEach((e, i) => {
        console.log(`${String(i + 1).padStart(2)}. ${e.date}  ${money(e.amount).padStart(12)}  ${e.description}`);
      });
      break;
    }

    case "total": {
      const total = expenses.reduce((sum, e) => sum + e.amount, 0);
      const biggest = expenses.reduce((max, e) => (e.amount > max.amount ? e : max), expenses[0]);
      console.log(`💸 Total spent: ${money(total)} across ${expenses.length} expenses`);
      if (biggest) console.log(`🏆 Biggest: ${money(biggest.amount)} on ${biggest.description}`);
      break;
    }

    case "clear": {
      await save([]);
      console.log("🧹 All expenses cleared.");
      break;
    }

    default:
      console.log("Commands: add <amount> <description> | list | total | clear");
  }
}

main();

Now try it out in the terminal:

Terminal
node expenses.js add 45 burger and chicken
node expenses.js add 12.50 bus fare
node expenses.js add 120 new sandals
node expenses.js list
node expenses.js total

Output

✅ Added $45.00 for "burger and chicken"
✅ Added $12.50 for "bus fare"
✅ Added $120.00 for "new sandals"
 1. 2026-10-15     $45.00  burger and chicken
 2. 2026-10-15     $12.50  bus fare
 3. 2026-10-15    $120.00  new sandals
💸 Total spent: $177.50 across 3 expenses
🏆 Biggest: $120.00 on new sandals

(Your dates will show today's date, of course.) Close the terminal, come back tomorrow, run node expenses.js list again, and your expenses are still there, safely saved in expenses.json. You've built a real, useful tool!

Try it yourself
  1. Add a "script" to package.json so that npm run list shows your expenses.
  2. Add a delete <number> command that removes one expense by its list number.
  3. Add a category to each expense (like food, transport, fun), and a summary command that shows the total for each category. (Hint: reduce into an object, from Grade 10.)

Reading is step one

CurlyScript turns these notes into a game: short questions, an explanation after every answer, and a test at the end of each grade. Free, with nothing to sign up for.

Start learning