A blazing fast, single-page expense tracking application built using Next.js App Router and a robust native SQLite database. Designed specifically without complex ORMs or UI libraries, emphasizing raw SQL and vanilla CSS.
- Single Page Application: Experience seamless operations. Actions automatically re-poll API routes to keep your DOM matched optimally with your backend without harsh page reloads.
- Optimistic UI Deletions: Deleting an expense removes the item from your screen instantly before pinging the backend, maximizing perceived speed.
- Dynamic Category Filtering: Toggle effortlessly through distinct expenditure profiles natively mapped via REST parameters (
?category=food). - Server Components Initiation: Initial page painting connects to the SQLite backend directly from Next.js server components, ensuring fully instantiated initial data fetches without loading spinners.
- Computed Spend Summations: Total spending is proactively computed in real time based strictly on the retrieved payload list visible on your dashboard.
- Robust Delete Protections: Native browser alert confirmations prevent accidental removals of historical expense logs.
- Frontend Framework: Next.js (App Router) + React
- Styling: Pure Vanilla CSS (Clean, box-model oriented architecture)
- Database: SQLite natively integrated via
better-sqlite3 - Typing: Strict TypeScript mapping schemas
The application adopts a robust separation of concerns, operating strictly through Next.js Route Handlers.
app/api/expenses/route.ts: Exposes powerfulGETandPOSThooks to manage your tracking list. Includes server-side validation rejecting invalid schema requests.app/api/expenses/[id]/route.ts: Contains specificDELETEdynamic operations executing localizedDELETE FROM expenses WHERE id = ?queries directly.lib/db.ts: Houses the absolute SQLite configuration connecting toexpenses.dblocally, running yourCREATE TABLE IF NOT EXISTSoperations natively on boot.app/components/ClientTracker.tsx: Main interactive state hub managing filter categories and binding inputs natively to dynamic Next endpoints.app/page.tsx: Initial server-oriented hook fetching SQLite logs transparently.
-
Clone the repository (if applicable) and navigate to the root directory.
-
Install all dependencies:
npm install- Start the local Next.js development server:
npm run dev- Initialize Your Application:
Open up http://localhost:3000 in your native browser. An
expenses.dbfile will automatically compile in your project root upon load containing your newly scaffolded tables!
GET /api/expenses- Fetches the current list.
- Optional Query:
?category=foodfilters outputs strictly based on constraints.
POST /api/expenses- Body:
{ title: string, amount: number, category: string, date: string } - Validates fields to ensure numerical validity on costs and string mappings on categorizations. Creates new UUID mapped records.
- Body:
DELETE /api/expenses/:id- Wipes out records based completely upon strictly isolated UUID definitions.
The architecture connects standard Next.js components to a local SQLite database, avoiding complex state-management libraries. Here's a breakdown of how the data flows and how you can alter it:
We use better-sqlite3 to generate a lightweight file called expenses.db directly inside your project root.
- Modifying the Schema: To add a new field (e.g.,
notesortag), update theCREATE TABLEexecution string natively insidelib/db.ts. You must subsequently update your typescript definitions insidelib/types.tsto ensure frontend type safety. Since there's no ORM, no complex migrations are required—although you may need to delete your existingexpenses.dbfile to let the script safely regenerate the new table logic on boot.
Next.js handles all isolated backend operations securely. The GET and POST handlers validate incoming payloads and pipe them as raw SQL commands directly to SQLite.
- Altering Backend Endpoints: If you want to implement new URL parameters (like sorting by total
amountinstead ofdate), open upapp/api/expenses/route.ts. Inside theGETfunction block, add a new variable parsing your parameter out ofsearchParams.get(...)and define your new conditionaldb.prepare(SELECT ... ORDER BY x)string.
The frontend visually updates entirely through standard React useState hooks mapped directly to DOM nodes.
- Understanding UI Flows: When users interact with buttons, we execute an immediate standard browser
fetch()calling the Next.js API. For instance,handleDeleteexecutes an "Optimistic UI Update". It physically rewrites your local React state to instantly hide the expense on the screen, pinging the database in the background via the API and silently reversing the state back if the server errors out. - Modifying Frontend Interactions: If you want to change what occurs when an item is submitted, edit the
handleAddSubmitfunction logic to inject new fields or to toggle loading statesetIsSubmittingtriggers.
This project operates under the standard MIT License.