Sổ Thơ Nụ is a lightweight web application designed to simplify personal and shared expense tracking for roommates, automating debt calculations and transaction logging.
Track who owes whom among friends and roommates, eliminating confusion from spreadsheets and manual calculations.
- Quick entry for debts and bills
- Real-time debt dashboard with filtering
- Automatic debt calculations and statistics
- Visual debt matrix showing net balances
- Light/dark theme support
- Vietnamese language interface
- Debt filtering by creditor, debtors, amount, and time
- Edit and delete existing bills
Trebt-IOU/
├── api/ # NestJS Backend
│ ├── src/
│ │ ├── bills/ # Bill management (CQRS)
│ │ ├── debts/ # Debt tracking
│ │ ├── statistics/ # Statistics calculation
│ │ ├── dtos/ # Data transfer objects
│ │ └── prisma/ # Prisma service
│ ├── prisma/
│ │ ├── schema/ # Prisma schema
│ │ └── migrations/ # Database migrations
│ └── package.json
├── web/ # React Frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── dtos/ # TypeScript DTOs
│ │ └── helper/ # Utility functions
│ └── package.json
└── README.md
- Framework: NestJS 11.0 with CQRS pattern
- Language: TypeScript 5.7
- Database: PostgreSQL with Prisma ORM 7.2
- API Documentation: Swagger (OpenAPI)
- Package Manager: pnpm
- Validation: class-validator and class-transformer
- Session Management: express-session with cookie-parser
- Framework: React 19.2 with TypeScript
- Build Tool: Vite 7.2
- Styling: Tailwind CSS v4.1
- Routing: React Router 7.11
- HTTP Client: Axios 1.13
- State Management: TanStack React Query 5.62
- Deployment: Vercel (frontend), Render (backend)
- User: User information (name) with relations to bills, debts, and statistics
- Bill: Expense records with creditor, debtors, type (SPLITTING/EACHONE), timestamps
- Debt: Individual debt relationships linking creditor, debtor, and bill
- Statistic: Aggregated debt statistics between users (totalLent, totalOwed)
- SPLITTING: Total amount divided equally among all debtors
- EACHONE: Each debtor owes the full amount
- PostgreSQL with Prisma ORM
- Custom client output:
@generated/prisma - Binary targets: native, linux-musl-openssl-3.0.x
- Preview features: fullTextSearchPostgres, postgresqlExtensions, views
- Node.js (v18 or higher)
- PostgreSQL
- pnpm (for backend) or npm (for frontend)
cd api
pnpm install
cp .env.example .envConfigure your .env file:
DATABASE_URL="postgresql://user:password@localhost:5432/trebt_iou"
PORT=3002
CORS_ORIGIN="http://localhost:5173"Run migrations:
pnpm prisma migrate devStart development server:
pnpm run devThe API will be available at http://localhost:3002/v1
cd web
npm installStart development server:
npm run devThe app will be available at http://localhost:5173
Build for production:
npm run buildPreview production build:
npm run previewSwagger UI is available at: http://localhost:3002/v1/docs
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/ |
Health check |
| POST | /v1/seed |
Seed initial data |
| POST | /v1/bills/add |
Add new bill |
| POST | /v1/bills/edit |
Edit existing bill |
| POST | /v1/bills/remove |
Remove bill |
| GET | /v1/bills/query |
Query bills with filters |
| GET | /v1/statistic |
Get debt statistics matrix |
creditorId: Filter by creditor ID (1-4)debtorIds: Filter by debtor IDs (comma-separated)lowerAmount: Minimum total amountupperAmount: Maximum total amounttimeAsc: Sort by creation time ascending (true/false)amountAsc: Sort by amount ascending (true/false)
| Route | Component | Description |
|---|---|---|
/dashboard |
Dashboard | View all bills with filter options |
/add-bill |
AddBill | Create new bills |
/statistic |
Statistic | View debt statistics matrix |
/filter |
Filter | Filter bills by creditor/debtor |
The application includes 4 default users:
- Phương (ID: 1)
- Pha (ID: 2)
- Thịnh (ID: 3)
- Tuấn (ID: 4)
pnpm run dev # Start in watch mode
pnpm run build # Build for production
pnpm run start:prod # Run production build
pnpm run test # Run unit tests
pnpm run test:e2e # Run e2e tests
pnpm run lint # Lint code with ESLint
pnpm run format # Format code with Prettier
pnpm run check-types # Type check with TypeScriptnpm run dev # Start dev server with Vite
npm run build # Build for production (TypeScript + Vite)
npm run preview # Preview production build
npm run lint # Lint code with ESLintBackend is deployed on Render:
- Base URL:
https://trebt-iou-api.onrender.com/v1 - PostgreSQL database on Render
- Environment variables:
DATABASE_URL,PORT,CORS_ORIGIN - Build command:
pnpm run build - Start command:
pnpm run start:prod
Frontend is configured for Vercel deployment:
- Build command:
npm run build - Output directory:
dist - SPA routing with rewrite rules
Deploy to Vercel:
- Push code to Git repository
- Connect repository to Vercel
- Vercel auto-detects and deploys
For sharing with roommates locally:
- Ngrok: Expose via
ngrok http 3002(API) orngrok http 5173(Frontend) - Local network: Use local IP (e.g.,
http://192.168.1.100:5173) - Update CORS_ORIGIN in backend
.envto allow access
The backend implements the CQRS (Command Query Responsibility Segregation) pattern:
Commands (write operations):
AddBillCommand: Create new billEditBillCommand: Update existing billDeleteBillCommand: Remove billAddDebtCommand,EditDebtCommand,RemoveDebtCommand: Debt operations
Queries (read operations):
BillQuery: Query bills with filtersQueryStatisticQuery: Get debt statistics
All commands and queries use separate handlers and DTOs for clear separation of concerns.
- Dashboard: Displays bills in a table with edit/delete options
- AddBill: Form to create new bills with creditor, debtors, amount, and type
- Statistic: Visual matrix showing net balances between users
- Filter: Advanced filtering by creditor, debtors, amount, and time
- ThemeSwitcher: Light/dark mode toggle
- Options/EditDialog: Dialogs for viewing and editing bill details
Private project