stashticly

β€’
3min
β€’
stashticly site

Introduction

Stashticly derived from the word stash β€” meaning to store something safely and secretlyβ€”is more than just an expense tracker. It's a powerful, yet user-friendly tool designed to help users take control of their financial lives and can serve as a starting point for creating custom expense tracker apps. This project originated from Antonio's video series, with frontend development by myself and a robust backend crafted by Gustio.

Features

  • πŸ“Š Interactive financial dashboard
  • πŸ“ˆ Changeable chart types
  • πŸ” Account and date filters
  • πŸ“‘ Detailed transactions table
  • βž• Form to add transactions
  • 🎨 Customizable select components
  • πŸ’° Income and expense toggle
  • πŸ—‘οΈ Bulk delete and search in transactions
  • πŸ“ CSV transaction imports

Tech Stack

Project Roadmap

Initial Setup

The project began with setting up Next.js and shadcn/ui with Bun aas the package manager, providing a solid foundation for frontend development.

Authentication with Clerk

Clerk was implemented for robust authentication, utilizing middleware to authenticate users before accessing sensitive data.

middleware.ts
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
 
const isProtectedRoute = createRouteMatcher(["/dashboard(.*)"]);
 
export default clerkMiddleware((auth, request) => {
  if (isProtectedRoute(request)) {
    auth().protect();
  }
 
  return NextResponse.next();
});
 
export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

API Routes with Hono

Hono was used for API routes, ensuring data security and proper validation.

route.ts
import { clerkMiddleware, getAuth } from "@hono/clerk-auth";
import { Hono } from "hono";
import { handle } from "hono/vercel";
 
export const runtime = "edge";
 
const app = new Hono().basePath("/api");
 
app.get("/hello", clerkMiddleware(), (c) => {
  const auth = getAuth(c);
  if (!auth?.userId) {
    return c.json({ error: "Unauthorized" }, 401);
  }
  return c.json({
    message: "Hello, World!",
    userId: auth.userId,
  });
});
 
export const GET = handle(app);
export const POST = handle(app);

Database Connection

Database connection was established using DrizzleORM and Neon Postgres:

drizzle.ts
import * as schema from "@/db/schema";
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
 
export const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });

API Structure and Data Management

Separate API route files were created for accounts, summaries, transactions, and categories. Tanstack Query was used to handle queries and mutations for CRUD operations in form components.

Interactive Dashboard

Recharts was used to create an interactive and visually appealing dashboard for tracking expenses and income.

Next Steps

  • Implement more advanced filtering and sorting options
  • Add support for multiple currencies
  • Integrate with popular financial institutions for automatic transaction imports
  • Implement budget planning and goal-setting features

Contributions from the open-source community are welcome as Stashticly continues to evolve!