Financial management API for class treasurers
Built with Bun, Express, TypeScript, PostgreSQL, Redis, and GCP Cloud Storage
Last Updated: February 5, 2026
- Overview
- Features
- Tech Stack
- Prerequisites
- Quick Start
- Project Structure
- Environment Variables
- API Documentation
- Development
- Deployment
- Troubleshooting
GalaCash is a comprehensive backend API for managing class finances. It provides two user roles:
- Students (user): View transactions, submit fund applications, pay monthly bills
- Treasurer (bendahara): Manage all finances, approve applications, confirm payments
Multi-Class Transparency Model: As of January 2026, both users and bendahara can view data across all classes within the same angkatan (batch). The classId field is retained for organizational purposes and future filtering capabilities.
✅ Authentication: NIM + Password with JWT tokens
✅ Fund Applications (Aju Dana): Submit and review funding requests
✅ Cash Bills (Tagihan Kas): Automated monthly bill generation
✅ Transactions: Track income and expenses across all classes
✅ Financial Reports (Rekap Kas): Comprehensive batch-level financial summaries
✅ File Uploads: GCP Cloud Storage integration for payment proofs
✅ Caching: Redis for improved performance
✅ Auto-transactions: Bills and approvals auto-create transactions
✅ Data Transparency: Aggregated views across all classes in a batch
✅ Payment Accounts: Manage bank accounts and e-wallets for treasury
✅ Transaction Labels: Organize transactions with custom labels
✅ Export Reports: Generate Excel reports for financial summaries
| Technology | Purpose |
|---|---|
| Bun 1.x | Runtime environment (Node.js compatible) |
| Express.js 5.x | Web framework |
| TypeScript | Type safety |
| PostgreSQL 16 | Primary database |
| Prisma v7 | ORM and migrations (with prisma-client provider) |
| @prisma/adapter-pg | PostgreSQL adapter for Prisma |
| Redis (ioredis) | Caching layer |
| JWT | Authentication (HS256) |
| GCP Cloud Storage | File uploads |
| Winston | Logging |
| Joi | Input validation |
| node-cron | Scheduled jobs |
| Helmet | Security headers |
| Express Rate Limit | Rate limiting (with Redis store) |
| Swagger UI | API documentation |
| ExcelJS | Export financial reports |
| Docker | Containerization |
Before you begin, ensure you have the following installed:
Optional:
- GCP Account (for file uploads - can be configured later)
Note: This project uses Bun as the runtime, which is a faster alternative to Node.js with full compatibility.
git clone https://github.com/ridwanalfarezi/galacash-server.git
cd galacash-serverbun install
# or using bun directly
bun installStart PostgreSQL and Redis:
docker-compose up -dVerify services are running:
docker-compose psThe .env file is already created for development. For production, copy and customize:
cp .env.example .env.productionRun migrations and seed data:
# Generate Prisma Client
bun prisma:generate
# Run database migrations
bun prisma:migrate
# Seed database with test data (automatic via migrations)
bun prisma:migrateThis will create:
- 2 classes (A, B)
- 83 students (40 in A, 43 in B)
- 1 bendahara (treasurer): NIM 1313624000
- Default password for all users:
12345678
Optional scripts for testing and development:
# Seed past cash bills (historical data)
bun run seed:past-bills
# Seed expense transactions
bun run seed:expenses
# Clean all data (⚠️ destroys all data)
bun run clean:databun devThe server will start on http://localhost:3000
Access:
- API Base:
http://localhost:3000/api - API Docs:
http://localhost:3000/api/docs - Health Check:
http://localhost:3000/health
🎉 You're ready!
galacash-server/
├── src/
│ ├── config/ # Configuration files
│ │ ├── redis.config.ts
│ │ ├── storage.config.ts
│ │ └── multer.config.ts
│ ├── controllers/ # HTTP request handlers
│ │ ├── auth.controller.ts
│ │ ├── bendahara.controller.ts
│ │ ├── cash-bill.controller.ts
│ │ ├── dashboard.controller.ts
│ │ ├── fund-application.controller.ts
│ │ ├── labels.controller.ts
│ │ ├── payment-account.controller.ts
│ │ ├── transaction.controller.ts
│ │ └── user.controller.ts
│ ├── routes/ # API route definitions
│ │ ├── auth.routes.ts
│ │ ├── bendahara.routes.ts
│ │ ├── cash-bill.routes.ts
│ │ ├── cron.routes.ts
│ │ ├── dashboard.routes.ts
│ │ ├── fund-application.routes.ts
│ │ ├── labels.routes.ts
│ │ ├── payment-account.routes.ts
│ │ ├── transaction.routes.ts
│ │ └── user.routes.ts
│ ├── services/ # Business logic layer
│ │ ├── auth.service.ts
│ │ ├── bendahara.service.ts
│ │ ├── cache.service.ts
│ │ ├── cash-bill.service.ts
│ │ ├── export.service.ts
│ │ ├── fund-application.service.ts
│ │ ├── payment-account.service.ts
│ │ ├── refresh-token.service.ts
│ │ ├── transaction.service.ts
│ │ └── user.service.ts
│ ├── repositories/ # Data access layer
│ │ ├── cash-bill.repository.ts
│ │ ├── fund-application.repository.ts
│ │ ├── payment-account.repository.ts
│ │ ├── refresh-token.repository.ts
│ │ ├── transaction.repository.ts
│ │ └── user.repository.ts
│ ├── middlewares/ # Express middlewares
│ │ ├── auth.middleware.ts
│ │ ├── rate-limit.middleware.ts
│ │ ├── upload.middleware.ts
│ │ └── validator.middleware.ts
│ ├── validators/ # Input validation schemas
│ │ └── schemas.ts
│ ├── utils/ # Utilities and helpers
│ │ ├── errors/ # Error handling & codes
│ │ ├── logger.ts # Winston logger
│ │ └── prisma-client.ts # Prisma singleton with adapter
│ ├── types/ # TypeScript type definitions
│ ├── jobs/ # Scheduled tasks
│ │ └── bill-generator.job.ts
│ ├── prisma/generated/ # Auto-generated Prisma Client
│ ├── app.ts # Express app configuration
│ └── index.ts # Application entry point
├── prisma/
│ ├── schema.prisma # Database schema (Prisma v7 format)
│ ├── seed.ts # Database seeding
│ └── migrations/ # Database migrations
├── scripts/ # Utility scripts
│ ├── seed-past-cash-bills.ts
│ ├── seed-expense-transactions.ts
│ └── clean-data.ts
├── tests/ # Test files
├── docs/ # Documentation
│ ├── API.md # API documentation
│ └── DATABASE.md # Database schema documentation
├── package.json
├── tsconfig.json
├── bunfig.toml # Bun configuration
├── Dockerfile # Production Docker image
├── Dockerfile.binary # Binary build Docker image
├── cloudbuild.yaml # GCP Cloud Build config
├── docker-compose.yml # Local development services
├── openapi.yaml # OpenAPI 3.0 specification
├── .env.example # Environment variables template
└── README.md
Key environment variables (see .env.example for complete list):
| Variable | Description | Default/Example |
|---|---|---|
NODE_ENV |
Environment | development |
PORT |
Server port | 3000 |
LOG_LEVEL |
Winston log level | info |
| Database | ||
DATABASE_URL |
PostgreSQL connection string | postgresql://galacash:galacash123@localhost:5433/galacash_db |
POSTGRES_URL |
Alternative PostgreSQL URL | Same as DATABASE_URL |
PRISMA_DATABASE_URL |
Prisma Accelerate URL (production) | prisma+postgres://... |
PRISMA_CLIENT_ENGINE_TYPE |
Prisma engine type | dataproxy (for Prisma Accelerate) |
| Redis | ||
REDIS_URL |
Redis connection string | redis://localhost:6379 |
| Authentication | ||
JWT_SECRET |
JWT access token secret | Generate with openssl rand -base64 64 |
JWT_REFRESH_SECRET |
JWT refresh token secret | Generate with openssl rand -base64 64 |
| GCP Cloud Storage (Optional) | ||
GCP_PROJECT_ID |
Google Cloud project ID | your-gcp-project-id |
GCP_BUCKET_NAME |
GCS bucket name | galacash-bucket |
GOOGLE_APPLICATION_CREDENTIALS |
Path to GCP service account key | /path/to/key.json or leave empty |
| Scheduled Jobs | ||
BILL_GENERATION_SCHEDULE |
Cron expression for monthly bills | 0 0 1 * * (1st day of month at midnight) |
USE_LOCAL_CRON |
Use node-cron (local only) | true (dev), false (production) |
CRON_SECRET_KEY |
Secret key for cron endpoint | Required for production Cloud Scheduler |
| App Configuration | ||
KAS_KELAS_AMOUNT |
Monthly class dues amount (IDR) | 10000 |
CORS_ORIGIN |
Allowed CORS origins (comma-sep) | http://localhost:5173,https://your-frontend.com |
openssl rand -base64 64Swagger UI is available at: http://localhost:3000/api/docs
Bendahara (Treasurer):
NIM: 1313624000
Password: 12345678
Role: bendahara
Students (Class A):
NIMs: 1313624001 - 1313624068 (40 students)
Password: 12345678
Role: user
Students (Class B):
NIMs: 1313624011 - 1313624085 (43 students)
Password: 12345678
Role: user
| Command | Description |
|---|---|
bun dev |
Start development server with hot reload |
bun run build |
Build for production |
bun run build:binary |
Build standalone binary with Bun |
bun start |
Start production server |
bun start:prod |
Deploy migrations and start server |
bun lint |
Run ESLint |
bun lint:fix |
Fix ESLint issues |
bun run format |
Format code with Prettier |
bun run type-check |
Run TypeScript type checking |
bun run commit |
Interactive commit with Commitizen |
| Database | |
bun prisma:generate |
Generate Prisma Client to src/prisma/ |
bun prisma:migrate |
Run database migrations (dev) |
bun prisma:deploy |
Deploy migrations (production) |
bun prisma:studio |
Open Prisma Studio (interactive DB GUI) |
bun seed |
Seed database with initial data |
bun seed:past-bills |
Seed historical cash bills |
bun seed:expenses |
Seed expense transactions |
bun run clean:data |
|
| Testing | |
bun test |
Run tests with Bun |
bun test:watch |
Run tests in watch mode |
bun test:up |
Start test database services |
bun test:down |
Stop test database services |
bun test:migrate |
Run migrations on test database |
View database with Prisma Studio:
bun prisma:studioCreate new migration:
bun prisma migrate dev --name your_migration_nameReset database (
bun prisma migrate resetThe project uses Prisma v7 with modern best practices:
- Provider:
prisma-client(notprisma-client-js) - Custom Output Path: Generated client lives in
src/prisma/generated/ - Adapter: Uses
@prisma/adapter-pgwith direct PostgreSQL connection - Accelerate: Uses
@prisma/extension-acceleratefor production (Prisma Accelerate) - Seeding: Automatic seeding via
seed: "bun prisma/seed.ts"in migrations config
This project uses Bun as the JavaScript runtime:
- Fast Execution: Significantly faster than Node.js
- Built-in TypeScript: No need for ts-node or tsx
- Hot Reload: Fast development with
bun --watch - Binary Compilation: Create standalone executables with
bun build --compile - Node.js Compatible: Works with existing Node.js packages
The codebase follows a clean layered architecture:
- Routes (
src/routes/) - API endpoint definitions - Controllers (
src/controllers/) - Request/response handling - Services (
src/services/) - Business logic - Repositories (
src/repositories/) - Data access - Middlewares (
src/middlewares/) - Request processing (auth, validation, etc.) - Validators (
src/validators/) - Joi schemas for input validation
- User logs in with NIM + Password
- Server validates credentials and hashes password with bcrypt
- JWT tokens generated:
- Access token (1 hour expiry)
- Refresh token (7 days expiry)
- Tokens stored in secure HTTP-only cookies or headers
- user: Regular student - can view own transactions and apply for funds
- bendahara: Treasurer - can manage all finances and approve applications
All endpoints use Joi schema validation via validator.middleware.ts:
- Validates request body, params, and query
- Returns 400 Bad Request with error details on validation failure
- Supports both application/json and application/x-www-form-urlencoded
Build the production image:
docker build -t galacash-server .Run the container:
docker run -p 3000:3000 \
-e DATABASE_URL="your_production_db_url" \
-e JWT_SECRET="your_jwt_secret" \
-e JWT_REFRESH_SECRET="your_refresh_secret" \
galacash-serverThe project includes Cloud Build configuration for automated deployment:
The cloudbuild.yaml file automates the deployment process:
- Installs dependencies
- Generates Prisma client
- Builds a standalone binary with Bun
- Creates a Docker image
- Pushes to Artifact Registry
- Deploys to Cloud Run
To deploy:
gcloud builds submit --config cloudbuild.yaml- Build and push image:
# Authenticate
gcloud auth configure-docker asia-southeast2-docker.pkg.dev
# Build image
docker build -t asia-southeast2-docker.pkg.dev/YOUR_PROJECT_ID/galacash-server/galacash-server .
# Push to Artifact Registry
docker push asia-southeast2-docker.pkg.dev/YOUR_PROJECT_ID/galacash-server/galacash-server- Deploy to Cloud Run:
gcloud run deploy galacash-server \
--image asia-southeast2-docker.pkg.dev/YOUR_PROJECT_ID/galacash-server/galacash-server \
--platform managed \
--region asia-southeast2 \
--allow-unauthenticated \
--set-env-vars "NODE_ENV=production,USE_LOCAL_CRON=false" \
--set-env-vars "PRISMA_CLIENT_ENGINE_TYPE=dataproxy"- Configure secrets:
Use Cloud Run Secret Manager for sensitive variables:
JWT_SECRETJWT_REFRESH_SECRETPRISMA_DATABASE_URL(Prisma Accelerate URL)REDIS_URL(Upstash or Cloud Redis)CRON_SECRET_KEYGCP_PROJECT_IDandGCP_BUCKET_NAME(for file uploads)
- Set up Cloud Scheduler:
Create a cron job to trigger bill generation:
gcloud scheduler jobs create http generate-monthly-bills \
--location=asia-southeast2 \
--schedule="0 0 1 * *" \
--uri="https://your-cloud-run-url/api/cron/generate-bills" \
--http-method=POST \
--headers="X-Cron-Secret=YOUR_CRON_SECRET_KEY"# Check Docker Desktop is running
docker --version
# View container logs
docker-compose logs postgres
docker-compose logs redis
# Restart containers
docker-compose restart# Verify PostgreSQL is running on port 5433
docker-compose ps
# Test connection (note: port 5433, not 5432)
docker exec -it galacash-postgres psql -U galacash -d galacash_db
# Reset database
bun prisma migrate resetThe Docker setup uses:
- PostgreSQL on port 5433 (to avoid default 5432 conflicts)
- Redis on port 6379
- Express on port 3000
To use different ports, edit docker-compose.yml and update .env.
- API Specification
- Database Schema
- OpenAPI Schema
- Prisma v7 Documentation
- Express.js Guide
- PostgreSQL Documentation
- ⚡ Fast Runtime: Uses Bun for blazing-fast performance
- 🔒 Type-Safe: Full TypeScript with strict mode enabled
- 📚 Well-Documented: Swagger UI, OpenAPI spec, and comprehensive README
- 🏗️ Modern Stack: Bun, Express 5.x, Prisma v7, PostgreSQL 16
- 🛡️ Production-Ready: Docker support, error handling, structured logging, rate limiting
- 👨💻 Developer-Friendly: Hot reload, Prisma Studio, formatted code, Git hooks
- 🧪 Testing: Bun test runner with test database setup
- 📦 Binary Compilation: Can be built as a standalone executable
- ☁️ Cloud-Native: Configured for Google Cloud Run deployment
- ♻️ Clean Architecture: Layered design with clear separation of concerns
We follow Conventional Commits and ensure code quality via Husky hooks.
- Fork and clone the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes and test thoroughly
- Commit using Commitizen:
This ensures your commit messages follow the standard:
bun run commit
<type>(<scope>): <subject>. - Push and create a Pull Request
Before committing, the following checks run automatically:
- Lint-staged: Runs
eslintandprettieron staged files. - Type-check: Runs
tsc --noEmitto ensure type safety.
Please see our Contributing Guide for more details.
ISC License - see LICENSE
If you encounter issues:
- Check the Troubleshooting section
- Review logs in
logs/error.log - Verify environment variables in
.env - Ensure Docker containers are running
- Check API documentation at
/api/docs
Happy coding! 🚀