A modern, full-stack toilet finder application serving Singapore with real-time location services, crowdsourced data, and intelligent geospatial search capabilities.
output.mp4
The Challenge: Finding accessible, clean public toilets in urban environments is a universal problem, especially for travelers, elderly, disabled individuals, and families with young children.
Our Solution: PeePal transforms toilet discovery through:
- Real-time geospatial search using advanced PostGIS technology
- Crowdsourced data with community-driven reviews and ratings
- Smart filtering by amenities (wheelchair accessible, bidet, shower, sanitizer)
- Intelligent auto-moderation system for data quality
- Turn-by-turn navigation with Apple MapKit integration
- PostGIS integration for efficient spatial queries and indexing
- Real-time location processing with radius-based toilet discovery
- Geocoding & reverse geocoding for address resolution
- Route optimization using Apple MapKit APIs
- RESTful API design with clear separation of concerns
- Type-safe APIs with comprehensive Zod validation
- JWT-based authentication with secure password hashing
- Auto-scaling database with proper indexing and constraints
- Docker containerization with multi-stage builds
- BLoC pattern for clean state management architecture
- Repository pattern for data abstraction
- Custom UI components with Material Design principles
- Responsive design across different screen sizes
- Real-time updates with optimistic UI patterns
- bcrypt password hashing with secure salt generation
- JWT token management with proper expiration
- Input sanitization and SQL injection prevention
- Rate limiting and DDoS protection
- Secure file uploads with MinIO object storage
Framework │ Hono (TypeScript) - High-performance web framework
Database │ PostgreSQL + PostGIS for geospatial data
ORM │ Drizzle ORM - Type-safe database operations
Authentication │ JWT with bcrypt password hashing
File Storage │ MinIO (S3-compatible) object storage
Testing │ Vitest for unit & integration testing
Validation │ Zod schemas for runtime type checking
Deployment │ Docker + GitHub Actions CI/CDFramework │ Flutter (Dart) - Cross-platform development
State Mgmt │ BLoC Pattern - Reactive programming
Maps │ Apple Maps (MapKit) - Native iOS integration
UI Components │ Custom Material Design + Cupertino widgets
HTTP Client │ Dio - Powerful HTTP client with interceptors
Local Storage │ SharedPreferences for cachingApple MapKit API │ Real-time location & navigation services
Google Maps API │ Additional geocoding capabilities
MinIO Storage │ Scalable image upload & processing
Geolocator │ GPS positioning with high accuracy
// Clean RESTful endpoints with semantic HTTP methods
GET /api/toilets/nearby // Geospatial search with filters
POST /api/toilets/create // Add new toilet location
PATCH /api/toilets/:id // Update existing toilet
DELETE /api/favorites/:id // Remove from favoritesRequest → CORS → Logger → Validator → Auth → Route Handler → Response-- Geospatial indexing for performance
CREATE INDEX toilets_location_idx ON toilets USING GIST(location);
-- Foreign key constraints for data integrity
ALTER TABLE reviews ADD CONSTRAINT fk_toilet
FOREIGN KEY (toilet_id) REFERENCES toilets(id);// Secure token generation with user context
const token = jwt.sign({ userId, email }, SECRET, {
expiresIn: '24h',
algorithm: 'HS256'
});// Clean separation of business logic and UI
class ToiletsBloc extends Bloc<ToiletEvent, ToiletState> {
ToiletsBloc(this._repository) : super(ToiletInitial()) {
on<LoadNearbyToilets>(_onLoadNearbyToilets);
on<FilterToilets>(_onFilterToilets);
}
}// Reusable, composable UI components
class ToiletCard extends StatelessWidget {
final Toilet toilet;
final VoidCallback onTap;
// Custom widget with Material Design principles
}- Spatial indexing reduces query time from seconds to milliseconds
- Efficient state management with BLoC pattern prevents unnecessary rebuilds
- Image optimization with automatic resizing and compression
- Database connection pooling for high concurrent user support
- Responsive caching strategies for offline functionality
- PostGIS spatial queries for accurate distance calculations
- Real-time location tracking with GPS accuracy optimization
- Dynamic radius search with customizable distance filters
- Route calculation with turn-by-turn navigation
- Address geocoding for human-readable location display
- Automatic content moderation with report-based deletion (3+ reports)
- SQL injection prevention through parameterized queries
- Password security with industry-standard bcrypt hashing
- Input validation at API boundary with Zod schemas
- Secure file uploads with type validation and virus scanning
- Crowdsourced data quality through community reviews
- Automated cleanup of stale or reported content
- Review aggregation with weighted rating calculations
- Favorites synchronization across devices
- Offline data caching for poor network conditions
// Comprehensive test coverage
├── Unit Tests │ Business logic validation
├── Integration Tests │ API endpoint testing
├── End-to-end Tests │ Complete user workflows
└── Database Tests │ Data integrity verification# Automated deployment workflow
Build → Test → Security Scan → Deploy → Health Check → Rollback Ready- TypeScript strict mode for compile-time safety
- ESLint & Prettier for consistent code formatting
- Conventional commits for clear git history
- Code review process with PR templates
- Documentation with inline comments and README
- Environment variable management for sensitive data
- CORS configuration for controlled access
- Rate limiting to prevent abuse
- HTTPS enforcement across all endpoints
- Dependency vulnerability scanning
- Find toilets within customizable radius (100m - 5km)
- Advanced filtering by amenities and accessibility features
- Real-time availability and crowd level indicators
- Smart sorting by distance, rating, and user preferences
- User-generated reviews with photo uploads
- Star ratings with weighted algorithms
- Community moderation through reporting system
- Crowdsourced toilet location submissions
- Turn-by-turn walking directions
- Wheelchair accessibility indicators
- Public transport integration
- Offline map caching for areas with poor connectivity
- Personal favorites list with cloud sync
- User preference learning for better recommendations
- Cross-device data synchronization
- Customizable notification settings
# Navigate to backend directory
cd backend
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your database and API keys
# Setup database
npm run generate # Generate migrations
npm run migrate # Apply migrations
npm run seed # Seed with initial data
# Start development server
npm run dev # http://localhost:3000# Navigate to Flutter app
cd peepal
# Install dependencies
flutter pub get
# Run on iOS Simulator
flutter run
# Build for production
flutter build iosPOST /auth/signup # User registration
POST /auth/login # User authenticationPOST /api/toilets/nearby # Geospatial search
POST /api/toilets/create # Add new toilet
GET /api/toilets/:id # Get toilet details
PATCH /api/toilets/:id # Update toilet info
POST /api/toilets/report/:id # Report toilet issuePOST /api/reviews/create # Add review
GET /api/reviews/toilet/:id # Get toilet reviews
PATCH /api/reviews/:id # Update review
DELETE /api/reviews/:id # Delete review
POST /api/reviews/report/:id # Report inappropriate reviewGET /api/favorites/me # Get user favorites
POST /api/favorites/add/:id # Add to favorites
DELETE /api/favorites/remove/:id # Remove favorite- Scalability: Microservices design allows independent scaling of components
- Maintainability: Clean separation of concerns with SOLID principles
- Performance: PostGIS spatial indexing provides sub-100ms query times
- Security: Enterprise-grade authentication and data protection
- Extensibility: Plugin architecture for easy feature additions
- Testing: Comprehensive test coverage ensures reliability
- DevOps: Automated CI/CD pipeline reduces deployment risks
- Repository Pattern for data access abstraction
- BLoC Pattern for predictable state management
- Middleware Pattern for cross-cutting concerns
- Observer Pattern for real-time UI updates
- Factory Pattern for object creation
- Dependency Injection for loose coupling
This project demonstrates expertise in full-stack development, system architecture, database design, mobile development, and DevOps practices. Built with production-ready code quality, comprehensive testing, and enterprise security standards.
Technical Skills Showcased:
- Full-Stack TypeScript Development
- Flutter Mobile App Development
- PostgreSQL & PostGIS Geospatial Computing
- RESTful API Design & Implementation
- JWT Authentication & Security
- Docker Containerization & Deployment
- CI/CD Pipeline Setup
- Database Design & Optimization
- State Management Architecture
- Testing & Quality Assurance
Built by Bryan Soong, Adam Soh, Joyce Lee, Liew Jia Wei, Joshua Tan
Transforming everyday problems into elegant technical solutions