Real-time theme park intelligence powered by machine learning
Aggregating wait times, weather forecasts, park schedules, and ML-powered predictions for optimal theme park experiences worldwide.
| Category | Technology |
|---|---|
| Backend | NestJS 11 ยท TypeScript (strict mode) |
| Database | PostgreSQL 16 ยท TimescaleDB |
| Cache & Queue | Redis 7 ยท Bull Queue |
| ML Service | Python 3.11 ยท CatBoost ยท FastAPI |
| DevOps | Docker Compose ยท GitHub Actions |
| Testing | Jest ยท Supertest ยท Testcontainers |
The API implements advanced logic to handle parks without official API schedules (e.g., Efteling, Hellendoorn) and seasonal closures.
hasOperatingSchedule FlagAll park objects include a hasOperatingSchedule boolean:
true: The park provides official operating hours via an API.false: The park does not provide official hours. Opening times are either null (future) or reconstructed from activity (past).For past days without official data, the system reconstructs opening hours from ride activity. See the Smart Gaps Documentation for technical details.
The system automatically identifies "Seasonal Parks" (parks with winter gaps > 21 days).
CLOSED (suppressing crowd predictions).UNKNOWN, allowing ML-powered crowd and wait-time predictions for trip planning.# Clone the repository
git clone https://github.com/PArns/v4.api.park.fan.git
cd v4.api.park.fan
# Install dependencies
npm install
# Copy environment configuration
cp .env.example .env
# Start infrastructure (PostgreSQL + Redis)
npm run docker:up
# Start development server
npm run dev
Once running, you can access:
System health checks and monitoring endpoints.
GET /v1/health # System health status
GET /v1/health/db # Database connectivity
Response includes:
Core endpoints for park information, weather, schedules, and wait times. All routes use the full geographic path structure for consistency and SEO-friendly URLs.
Geographic Routes:
GET /v1/parks # List all parks (paginated)
GET /v1/parks/:continent # Parks by continent
GET /v1/parks/:continent/:country # Parks by country
GET /v1/parks/:continent/:country/:city # Parks by city
GET /v1/parks/:continent/:country/:city/:parkSlug # Get park by location
GET /v1/parks/:continent/:country/:city/:parkSlug/calendar # Integrated calendar
GET /v1/parks/:continent/:country/:city/:parkSlug/weather # Current weather & history
GET /v1/parks/:continent/:country/:city/:parkSlug/weather/forecast # 16-day forecast
GET /v1/parks/:continent/:country/:city/:parkSlug/schedule # Operating hours
GET /v1/parks/:continent/:country/:city/:parkSlug/wait-times # Live wait times
GET /v1/parks/:continent/:country/:city/:parkSlug/predictions/yearly # Yearly crowd predictions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions # List park attractions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions/:attractionSlug # Get attraction
Query Parameters:
continent, country, city โ Filter by location (in list endpoint)sort โ Sort order (name, popularity, etc.)page, limit โ Pagination (default: page=1, limit=10)from, to โ Date range for weather/schedule (YYYY-MM-DD format)Example Geographic Route:
GET /v1/parks/north-america/united-states/orlando/magic-kingdom
Example Response:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Magic Kingdom",
"slug": "magic-kingdom",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom",
"continent": "North America",
"country": "United States",
"city": "Orlando",
"timezone": "America/New_York",
"currentStatus": "OPERATING",
"currentLoad": {
"crowdLevel": "moderate",
"occupancy": 0.65
},
"coordinates": { "lat": 28.3772, "lng": -81.5707 },
"attractions": [
{
"id": "...",
"name": "Space Mountain",
"slug": "space-mountain",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom/attractions/space-mountain"
}
]
}
Detailed attraction data with ML predictions and historical analytics. Access attractions through their park's geographic route.
Routes:
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions # List park attractions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions/:attractionSlug # Get attraction details
Query Parameters:
page, limit โ Pagination for attraction list (default: page=1, limit=10)Response includes:
Example Response:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Space Mountain",
"slug": "space-mountain",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom/attractions/space-mountain",
"park": {
"id": "...",
"name": "Magic Kingdom",
"slug": "magic-kingdom"
},
"category": "RIDE",
"currentWaitTime": 45,
"status": "OPERATING",
"lastUpdate": "2025-12-23T19:30:00Z",
"forecast": [
{ "hour": "20:00", "predictedWaitTime": 50, "confidence": 0.87 },
{ "hour": "21:00", "predictedWaitTime": 35, "confidence": 0.82 }
],
"stats": {
"averageWaitTime": 42,
"p50": 40,
"p75": 55,
"p90": 70
}
}
Navigate parks by geographic hierarchy for route generation and exploration.
GET /v1/discovery/geo # Full geo hierarchy
GET /v1/discovery/continents # List continents
GET /v1/discovery/continents/:continent # Countries in continent
Response Structure:
{
"continents": [
{
"continent": "North America",
"countries": [
{
"country": "United States",
"cities": [
{
"city": "Orlando",
"parks": [
{
"name": "Magic Kingdom",
"url": "/north-america/united-states/orlando/magic-kingdom",
"attractions": [...]
}
]
}
]
}
]
}
]
}
Resort-level aggregation grouping multiple parks. Destinations are used internally for data organization but are not exposed as top-level API endpoints. Parks are accessed directly via their geographic routes.
Examples:
Entertainment and dining options are available through the park endpoints. Shows and restaurants are not exposed as top-level API endpoints but are included in park responses and can be accessed via the search endpoint.
Global search with enriched results across parks and attractions.
GET /v1/search?q=disney # Search all types
GET /v1/search?q=thunder&type=attraction # Filter by type
GET /v1/search?q=paris # Search by city
Search Features:
Response Structure:
{
"query": "disney",
"counts": {
"park": {"returned": 5, "total": 13},
"attraction": {"returned": 5, "total": 156}
},
"results": [
{
"type": "park",
"name": "Disneyland Park",
"status": "OPERATING",
"load": "normal",
"parkHours": {...},
"coordinates": {...}
},
{
"type": "attraction",
"name": "Space Mountain",
"waitTime": 45,
"load": "higher",
"parentPark": {...}
}
]
}
Public holiday data affecting park crowds and operating hours. Holidays are used internally for ML predictions and analytics but are not exposed as top-level API endpoints.
v4.api.park.fan/
โโโ src/
โ โโโ config/ # App configuration & environment
โ โโโ common/ # Shared utilities, filters, interceptors
โ โโโ database/ # Database utilities & migrations
โ โโโ queues/ # Bull queue setup & processors
โ โโโ health/ # Health check endpoints
โ โโโ destinations/ # Resort/destination grouping
โ โโโ parks/ # Parks, weather, schedules
โ โโโ attractions/ # Attractions & data sources
โ โโโ shows/ # Entertainment shows
โ โโโ restaurants/ # Dining locations
โ โโโ queue-data/ # Wait time data & history
โ โโโ ml/ # ML prediction integration
โ โโโ analytics/ # Statistics & analytics
โ โโโ holidays/ # Public holiday data
โ โโโ date-features/ # Date-based features for ML
โ โโโ discovery/ # Geographic discovery endpoints
โ โโโ search/ # Global search functionality
โโโ ml-service/ # Python ML service (CatBoost)
โ โโโ train.py # Model training script
โ โโโ inference.py # FastAPI prediction service
โ โโโ features.py # Feature engineering
โ โโโ db.py # Database connection
โโโ docker/ # Docker configurations
โโโ scripts/ # Utility & debug scripts
โโโ migrations/ # Database migrations
โโโ test/ # E2E tests
# Start all services (PostgreSQL + Redis)
npm run docker:up
# Stop all services
npm run docker:down
# View logs
npm run docker:logs
# Restart services
npm run docker:restart
# Reset database (WARNING: deletes all data)
npm run db:reset
Production Deployment:
docker-compose -f docker-compose.production.yml up -d
# Run unit tests
npm run test
# Run e2e tests
npm run test:e2e
# Run all tests with coverage
npm run test:all:cov
# Watch mode for development
npm run test:watch
# Specific test file
npm run test -- wait-times.processor.spec.ts
Code Quality:
# Lint code
npm run lint
# Format code
npm run format
# Type check
npm run build
NODE_ENV=development # development | production | test
PORT=3000 # API server port
API_PREFIX=v1 # API version prefix
# PostgreSQL with TimescaleDB
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=parkfan
DB_PASSWORD=your_secure_password
DB_DATABASE=parkfan
DB_SYNCHRONIZE=true # โ ๏ธ Set to false in production!
DB_LOGGING=false # Enable for debugging
DB_SSL_ENABLED=false # Enable for production
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD= # Optional, recommended for production
# Bull Queue
BULL_PREFIX=parkfan
# Google APIs (Geocoding, Places)
GOOGLE_API_KEY=your_google_api_key
# Weather Data
OPEN_WEATHER_API_KEY=your_openweather_key
# Data Sources (optional, for enhanced coverage)
QUEUE_TIMES_API_KEY= # Queue-Times.com
THEMEPARKS_API_KEY= # ThemeParks.wiki
# ML Service Configuration
ML_SERVICE_URL=http://localhost:8000 # Development
# ML_SERVICE_URL=http://ml-service:8000 # Production (Docker)
MODEL_DIR=/app/models # Model storage directory
MODEL_VERSION=v1.1.0 # Current model version
# Data Sync Intervals (cron expressions)
SYNC_WAIT_TIMES_CRON=*/5 * * * * # Every 5 minutes
SYNC_PARK_METADATA_CRON=0 6 * * * # Daily at 6 AM
SYNC_WEATHER_CRON=0 * * * * # Hourly
This is a private project. For questions or collaboration inquiries, please contact the maintainer.
UNLICENSED โ Private project by Patrick Arns
This project aggregates data from multiple sources:
Special thanks to these services for making real-time theme park data accessible.
Made with โค๏ธ for theme park enthusiasts worldwide