A production-ready Leave Management System designed for startups with ~50 employees. Built with Node.js, Express, and Supabase PostgreSQL.
- β Add new employees with auto-generated employee IDs (EMP0001, EMP0002, etc.)
- β Initialize default leave balance (20 leaves/year)
- β Employee profile management
- β Department-wise organization
- β Apply for leave with business day calculations
- β Approve/Reject leave applications with audit trails
- β Real-time leave balance tracking
- β Leave history and statistics
- β Calendar view for leave planning
- β Comprehensive edge case handling
- β Business day calculation (excluding weekends)
- β Overlapping leave detection
- β Insufficient balance validation
- β Pre-joining date validation
- β Comprehensive API documentation with Swagger
- Backend: Node.js with Express.js
- Database: PostgreSQL via Supabase
- Validation: Joi for request validation
- Documentation: Swagger/OpenAPI 3.0
- Security: Helmet, CORS, Rate limiting
- Deployment: Ready for Render/Heroku/Vercel
- Node.js (v14 or higher)
- Supabase account and project
- npm or yarn package manager
-
Clone and Install
git clone <repository-url> cd mini-leave-management-system npm install
-
Environment Setup
cp .env.example .env
Update
.envwith your Supabase credentials:VITE_SUPABASE_URL=your_supabase_url_here VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here PORT=3000 NODE_ENV=development DEFAULT_LEAVE_BALANCE=20
-
Database Setup
- The migration file will automatically create the required tables and sample data
- Tables:
employees,leaves - Functions: Auto-generate employee IDs, calculate business days
- Sample employees are pre-loaded for testing
-
Start the Server
npm run dev
-
Access the API
- API Base URL:
http://localhost:3000/api - API Documentation:
http://localhost:3000/api-docs - Health Check:
http://localhost:3000/health
- API Base URL:
POST /api/employees- Add new employeeGET /api/employees- Get all employees (paginated)GET /api/employees/:id- Get employee by IDGET /api/employees/employee-id/:employeeId- Get employee by employee IDGET /api/employees/:id/leave-balance- Get employee leave balanceGET /api/employees/:id/leaves- Get employee's leave history
POST /api/leaves/apply- Apply for leaveGET /api/leaves- Get all leaves (with filtering)GET /api/leaves/:id- Get specific leavePOST /api/leaves/approve/:id- Approve leavePOST /api/leaves/reject/:id- Reject leaveGET /api/leaves/balance/:employee_id- Get leave balanceGET /api/leaves/pending- Get pending leaves (HR view)GET /api/leaves/calendar- Get leaves by date range
curl -X POST http://localhost:3000/api/employees \
-H "Content-Type: application/json" \
-d '{
"name": "Sonu Anand",
"email": "sonu@example.com",
"department": "Engineering",
"joining_date": "2025-08-01"
}'Response:
{
"success": true,
"message": "Employee added successfully",
"data": {
"id": "uuid",
"employee_id": "EMP0001",
"name": "Sonu Anand",
"email": "sonu@example.com",
"department": "Engineering",
"joining_date": "2025-08-01",
"leave_balance": 20
}
}curl -X POST http://localhost:3000/api/leaves/apply \
-H "Content-Type: application/json" \
-d '{
"employee_id": "employee-uuid",
"start_date": "2025-08-10",
"end_date": "2025-08-12",
"reason": "Family trip"
}'Success Response:
{
"success": true,
"message": "Leave application submitted successfully",
"data": {
"id": "leave-uuid",
"employee_id": "employee-uuid",
"start_date": "2025-08-10",
"end_date": "2025-08-12",
"reason": "Family trip",
"status": "pending",
"days_requested": 3
}
}Error Response (Insufficient Balance):
{
"success": false,
"error": "Insufficient leave balance",
"details": "You have 5 days remaining, but requested 10 days",
"available_balance": 5,
"requested_days": 10
}curl http://localhost:3000/api/leaves/balance/employee-uuidResponse:
{
"success": true,
"data": {
"employee": {
"id": "employee-uuid",
"employee_id": "EMP0001",
"name": "Sonu Anand",
"email": "sonu@example.com",
"department": "Engineering"
},
"leave_balance": 17,
"leave_stats": {
"total_applied": 3,
"pending": 1,
"approved": 2,
"rejected": 0,
"total_days_taken": 3
}
}
}{
"success": false,
"error": "Invalid leave dates",
"details": "Cannot apply for leave before joining date"
}{
"success": false,
"error": "Insufficient leave balance",
"details": "You have 5 days remaining, but requested 10 days",
"available_balance": 5,
"requested_days": 10
}{
"success": false,
"error": "Overlapping leave request",
"details": "You already have a pending or approved leave request for the specified dates",
"overlapping_leaves": [...]
}{
"success": false,
"error": "Invalid leave dates",
"details": "End date must be after or equal to start date"
}{
"success": false,
"error": "Employee not found",
"details": "No employee found with the provided ID"
}- id (UUID, Primary Key)
- employee_id (Text, Unique, Auto-generated: EMP0001, EMP0002...)
- name (Text, Required)
- email (Text, Unique, Required)
- department (Text, Required)
- joining_date (Date, Required)
- leave_balance (Integer, Default: 20)
- created_at, updated_at (Timestamps)- id (UUID, Primary Key)
- employee_id (UUID, Foreign Key)
- start_date, end_date (Dates, Required)
- reason (Text, Required)
- status (Enum: pending, approved, rejected)
- days_requested (Integer, Auto-calculated)
- applied_at (Timestamp)
- processed_at (Timestamp)
- processed_by (Text)
- comments (Text)- Default balance: 20 days per year
- Business days only (excludes weekends)
- Balance deducted only on approval
- Balance restored if approved leave is later rejected
- Employee ID: Auto-generated (EMP0001, EMP0002...)
- Business Days: Calculated excluding weekends
- Leave Balance: Updated via database triggers
- Timestamps: Automatic tracking of application and processing times
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
PORT=3000
NODE_ENV=development
DEFAULT_LEAVE_BALANCE=20- 100 requests per 15 minutes per IP
- Configurable in
src/server.js
- Helmet for security headers
- CORS enabled
- Input validation with Joi
- SQL injection prevention via Supabase
- Row Level Security (RLS) enabled
- Connect your GitHub repository
- Set environment variables
- Deploy automatically
heroku create your-app-name
heroku config:set VITE_SUPABASE_URL=your_url
heroku config:set VITE_SUPABASE_ANON_KEY=your_key
git push heroku main- Install Vercel CLI:
npm i -g vercel - Run:
vercel - Set environment variables in dashboard
- 20 days annual leave balance
- Business days exclude weekends only (no holidays)
- Single leave type (can be extended)
- Manual approval process (no auto-approval)
- Email notifications not included
- Role-based Access Control: HR, Manager, Employee roles
- Email Notifications: Automated notifications for applications/approvals
- Holiday Calendar: Country/region-specific holidays
- Multiple Leave Types: Sick, vacation, personal, etc.
- Leave Carryover: Unused leaves to next year
- Mobile App: React Native mobile application
- Analytics Dashboard: Leave trends and insights
- Bulk Operations: Approve/reject multiple leaves
- Leave Policies: Configurable leave policies per department
- Integration: Calendar apps, HR systems integration
npm testcurl http://localhost:3000/healthThe system includes 5 sample employees for testing:
- John Doe (Engineering)
- Jane Smith (HR)
- Mike Johnson (Marketing)
- Sarah Wilson (Finance)
- David Brown (Engineering)
- API Documentation: Available at
/api-docswhen server is running - Postman Collection: Can be generated from Swagger documentation
- Database Schema: Documented in migration files
For technical support or feature requests:
- Check the API documentation at
/api-docs - Review the error response format for debugging
- Ensure all environment variables are properly set
- Verify Supabase connection and permissions
- v1.0.0: Initial MVP release with core functionality
- Employee management
- Leave application and approval
- Balance tracking
- Comprehensive validation
- API documentation
Built with β€οΈ for efficient leave management