Trace a Transaction Through the App
Open these files in this order:
client/src/pages/LedgerPage.jsx
client/src/hooks/useMonthTransactions.js
client/src/context/BudgetContext.jsx
client/src/lib/api.js
server/src/app.js
server/src/routes/transactions.js
server/src/db.js
We are not changing code in this lesson. We are tracing one real transaction so the next lessons have a clear mental model.
1. Start at the page
LedgerPage.jsx assembles the main Ledger screen. It does not fetch data itself. Instead, it places components such as SummaryStrip, EntryForm, and TransactionList on the page.
The page is therefore the composition layer: it decides which pieces appear together.
2. Find the filtered records
useMonthTransactions.js reads transactions and cursor from useBudget() and produces the transactions for the selected month.
The important distinction is:
transactions = the complete client-side collection
cursor = the month the user is looking at
monthTx = the subset to render now
The filter does not delete or modify the complete transaction collection. It creates a view of it.
3. Follow the shared state
BudgetContext.jsx owns the client-side collections:
const [transactions, setTransactions] = useState([]);
const [categories, setCategories] = useState([]);
It also owns cursor, which tells the application which month is selected.
The context is the shared bridge between the pages/components and the API functions.
4. Follow the API call
client/src/lib/api.js contains the functions that make HTTP requests. For example:
export const getTransactions = () => request("/transactions");
The component does not need to know the server’s port or how fetch is configured. It asks the API helper for transactions.
5. Find the server route
server/src/app.js mounts the transaction router here:
app.use("/api/transactions", transactionsRouter);
That means a client request for /api/transactions is handled by server/src/routes/transactions.js.
The GET route reads the database and returns its transactions collection:
router.get("/", async (req, res) => {
const db = await getDb();
res.json(db.transactions);
});
getDb() comes from server/src/db.js. That module loads server/data/db.json into memory for the server to use.
Put the whole path together
server/data/db.json
↓
db.js
↓
transactions route
↓
api.js
↓
BudgetContext
↓
useMonthTransactions
↓
Ledger UI
The response moves upward through these layers. Each layer has one job, which keeps the application easier to understand and change.
Test it
Reload the Ledger and open your browser’s Network panel.
Select the request for /api/transactions and verify that its response contains the same transaction records you saw in server/data/db.json.
Checkpoint
You can follow one transaction from the JSON file all the way to the row rendered by the Ledger, and you know what each layer contributes along the way.