A modern, full-stack expense tracking application inspired by Splitwise, built with React, Node.js, Express, and SQLite.
- Create expense groups with multiple users
- View group details including members and total expenses
- Intuitive group overview with member avatars
- Add expenses with detailed descriptions and amounts
- Support for two split types:
- Equal Split: Automatically divide expenses evenly among group members
- Percentage Split: Custom percentage allocation for each member
- Track who paid for each expense
- Comprehensive expense history with timestamps
- Real-time calculation of who owes whom
- Simplified balance resolution (nets out mutual debts)
- Personal balance dashboard across all groups
- Visual indicators for debts and credits
- Create and manage users
- User avatars with initials
- User selection for personal balance views
- Clean, responsive design with Tailwind CSS
- Smooth animations and transitions
- Intuitive navigation with clear visual hierarchy
- Mobile-optimized interface
- Beautiful color system with semantic states
- React 18 with TypeScript
- Tailwind CSS for styling
- Lucide React for icons
- Vite for development and building
- Node.js with Express
- SQLite database for persistence
- TypeScript for type safety
- RESTful API design
- Node.js (v16 or higher)
- npm or yarn
- Docker and Docker Compose (for containerized setup)
-
Install dependencies:
npm install
-
Start the application:
npm run dev
This command starts both the backend API server (port 3001) and the frontend development server (port 5173) concurrently.
-
Access the application: Open your browser and navigate to
http://localhost:5173
-
Quick setup with script:
./scripts/docker-setup.sh
-
Or manually with Docker Compose:
# Build and start all services docker-compose up -d # View logs docker-compose logs -f # Stop services docker-compose down
-
Access the application:
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:3001 - Production (with Nginx):
http://localhost:80
- Frontend:
For development with hot reload:
docker-compose -f docker-compose.dev.yml up -d- backend: Node.js API server with SQLite database
- frontend: React app served by Nginx
- nginx: Reverse proxy for production deployment (optional)
- backend-dev: Node.js with hot reload and volume mounting
- frontend-dev: Vite dev server with hot reload
http://localhost:3001
GET /groups- Get all groupsPOST /groups- Create a new group{ "name": "Weekend Trip", "user_ids": ["user-1", "user-2", "user-3"] }GET /groups/{group_id}- Get group details with members and expenses
-
POST /groups/{group_id}/expenses- Add expense to group{ "description": "Dinner at restaurant", "amount": 120.50, "paid_by": "user-1", "split_type": "equal" }For percentage splits:
{ "description": "Hotel room", "amount": 200.00, "paid_by": "user-1", "split_type": "percentage", "splits": [ {"user_id": "user-1", "percentage": 40}, {"user_id": "user-2", "percentage": 35}, {"user_id": "user-3", "percentage": 25} ] }
GET /groups/{group_id}/balances- Get group balance summaryGET /users/{user_id}/balances- Get personal balances across all groups
GET /users- Get all usersPOST /users- Create a new user{ "name": "John Doe" }
- users: User information (id, name, created_at)
- groups: Group information (id, name, created_at)
- group_users: Many-to-many relationship between groups and users
- expenses: Expense records (id, group_id, description, amount, paid_by, split_type, created_at)
- expense_splits: Individual split amounts for each user per expense
├── src/
│ ├── components/ # React components
│ │ ├── GroupList.tsx # Groups overview
│ │ ├── CreateGroup.tsx # Group creation form
│ │ ├── GroupDetail.tsx # Group details and balances
│ │ ├── AddExpense.tsx # Expense creation form
│ │ ├── PersonalBalances.tsx # User balance dashboard
│ │ └── UserManagement.tsx # User creation and management
│ ├── App.tsx # Main application component
│ └── main.tsx # Application entry point
├── server/
│ ├── index.ts # Express server and API routes
│ └── database.ts # SQLite database setup and utilities
├── docker-compose.yml # Production Docker setup
├── docker-compose.dev.yml # Development Docker setup
├── Dockerfile.backend # Backend container configuration
├── Dockerfile.frontend # Frontend container configuration
├── nginx.conf # Nginx reverse proxy configuration
└── README.md
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f [service-name]
# Stop services
docker-compose down
# Rebuild and restart
docker-compose up -d --build
# Scale services
docker-compose up -d --scale backend=2# Start development environment
docker-compose -f docker-compose.dev.yml up -d
# View development logs
docker-compose -f docker-compose.dev.yml logs -f
# Stop development environment
docker-compose -f docker-compose.dev.yml down# Execute commands in running containers
docker-compose exec backend sh
docker-compose exec frontend sh
# View container status
docker-compose ps
# Remove all containers and volumes
docker-compose down -v --remove-orphansNODE_ENV: Environment (development/production)PORT: Server port (default: 3001)
VITE_API_URL: Backend API URL (default: http://localhost:3001)
The application implements sophisticated balance resolution:
- Tracks all expense splits per user
- Calculates gross amounts owed between each pair of users
- Nets out mutual debts to minimize transactions
- Provides simplified "who owes whom" summary
- Equal Split: Automatically divides expense amount by number of group members
- Percentage Split: Allows custom percentage allocation with validation (must total 100%)
- Balances update immediately after adding expenses
- Group totals recalculate automatically
- Personal balance dashboard reflects all group changes
npm run dev- Start both frontend and backend in development modenpm run dev:client- Start only the frontend development servernpm run dev:server- Start only the backend API servernpm run build- Build the frontend for productionnpm run preview- Preview the production build
The SQLite database (splitwise.db) is automatically created in the project root. In Docker, it's persisted in the ./data volume. It includes:
- Automatic table creation on first run
- Sample user data for testing
- Foreign key constraints for data integrity
- Clone the repository
- Run
docker-compose up -d - Access via
http://localhost:80(with Nginx) orhttp://localhost:5173(direct)
- Build frontend:
npm run build - Serve
dist/folder with any static file server - Deploy backend to any Node.js hosting service
- Configure environment variables
- No Authentication: The application doesn't implement user authentication for simplicity
- No Payments: Focus is on expense tracking and balance calculation, not actual payment processing
- SQLite Database: Uses file-based SQLite instead of PostgreSQL for easier setup in development
- Sample Users: Pre-seeds database with sample users for immediate testing
- Single Currency: All amounts are assumed to be in USD
- No Expense Editing: Expenses cannot be modified after creation (common in expense tracking apps for audit trail)
- User authentication and authorization
- Expense editing and deletion
- Multiple currency support
- Payment/settlement tracking
- Email notifications
- Mobile app
- Group chat/comments
- Receipt photo uploads
- Expense categories and tags
- Data export functionality
- PostgreSQL support for production
- Redis caching for better performance
- WebSocket support for real-time updates
- Port conflicts: Change ports in docker-compose.yml if 3001 or 5173 are in use
- Database permissions: Ensure the
./datadirectory has proper write permissions - Docker build fails: Clear Docker cache with
docker system prune -a - Services not starting: Check logs with
docker-compose logs [service-name]
The backend service includes health checks. Monitor with:
docker-compose psBuilt with ❤️ using modern web technologies and Docker


