Skip to content

PArns/v4.api.park.fan

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

864 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎒 park.fan API v4

Real-time theme park intelligence powered by machine learning

NestJS TypeScript PostgreSQL Docker

Aggregating wait times, weather forecasts, park schedules, and ML-powered predictions for optimal theme park experiences worldwide.


πŸ“– Quick Navigation


✨ Features

  • πŸš€ Real-time Wait Times β€” Live queue data for attractions, shows, and restaurants
  • πŸ€– ML Predictions β€” Machine learning forecasts for wait times and crowd levels
  • 🌀️ Weather Integration β€” Current conditions and 16-day forecasts for all parks
  • πŸ“… Park Schedules β€” Opening hours, special events, and operating calendars
  • πŸ•΅οΈ Smart Gaps β€” Reconstruction of historical opening hours from ride activity when official data is missing
  • 🌍 Multi-Source Data β€” Aggregated from multiple providers for maximum coverage
  • πŸ“Š Analytics Ready β€” TimescaleDB-powered time-series data for insights
  • ⚑ High Performance β€” Redis caching and Bull queue processing
  • 🎯 RESTful API β€” Clean endpoints with full Swagger/OpenAPI documentation

πŸ› οΈ Tech Stack

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

πŸ“… Special Logic: Schedules & Seasons

The API implements advanced logic to handle parks without official API schedules (e.g., Efteling, Hellendoorn) and seasonal closures.

1. hasOperatingSchedule Flag

All 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).

2. Historical Hour Reconstruction

For past days without official data, the system reconstructs opening hours from ride activity. See the Smart Gaps Documentation for technical details.

  • Opening Time: First 15min window with >= 10% ride activity, rounded down.
  • Closing Time: Last 15min window with activity, rounded up.
  • Rounding: Both times are rounded to the nearest full hour for maximum plausibility.

3. Seasonal Detection

The system automatically identifies "Seasonal Parks" (parks with winter gaps > 21 days).

  • Seasonal Parks: Future dates outside the known operating range are marked as CLOSED (suppressing crowd predictions).
  • Year-Round Parks: Future dates without schedules remain UNKNOWN, allowing ML-powered crowd and wait-time predictions for trip planning.

πŸš€ Quick Start

Prerequisites

  • Node.js 20+
  • Docker & Docker Compose
  • Git

Installation

# 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

Access Points

Once running, you can access:


πŸ“š API Documentation

πŸ₯ Health & Monitoring

System health checks and monitoring endpoints.

GET /v1/health              # System health status
GET /v1/health/db           # Database connectivity

Response includes:

  • System uptime
  • Database connection status
  • Last sync timestamps (wait times, park metadata)
  • Active jobs and queue status

🎑 Parks

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"
    }
  ]
}

🎒 Attractions

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:

  • Live wait times and status (OPERATING, CLOSED, DOWN, REFURBISHMENT)
  • 24-hour ML-powered wait time forecasts
  • Daily predictions with confidence scores
  • Historical statistics (average, percentiles)
  • Downtime tracking and reliability metrics
  • Full geographic URL for easy navigation

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
  }
}

🌍 Geographic Discovery

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",
                  "status": "OPERATING",
                  "analytics": {
                    "statistics": {
                      "avgWaitTime": 35,
                      "operatingAttractions": 42,
                      "closedAttractions": 3,
                      "totalAttractions": 45,
                      "crowdLevel": "moderate"
                    }
                  },
                  "attractions": [...]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Note: analytics.statistics.crowdLevel is co-present with avgWaitTime whenever the park has a valid P50 baseline. See P50 Crowd Levels for the rating system.


🏰 Destinations

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:

  • Walt Disney World (Magic Kingdom, EPCOT, Hollywood Studios, Animal Kingdom)
  • Disneyland Paris (Disneyland Park, Walt Disney Studios Park)

🎭 Shows & Dining

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.


πŸ” Intelligent Search

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:

  • Multi-entity search: Parks and attractions
  • Geographic search: By city, country, or continent
  • Per-type counts: Shows returned vs total results
  • Enriched results: Coordinates, wait times, park hours, full geographic URLs
  • Smart filtering: Type-based filtering with max 5 results per type
  • Fast response: Redis-cached for 5min, <3ms cached response

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": {...}
    }
  ]
}

πŸŽ‰ Holidays

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.


πŸ“ Project Structure

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

🐳 Docker Commands

# 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

πŸ§ͺ Testing

# 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

πŸ”§ Environment Variables

Application Settings

NODE_ENV=development              # development | production | test
PORT=3000                         # API server port
API_PREFIX=v1                     # API version prefix

Database Configuration

# 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

Caching & Queue

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=                   # Optional, recommended for production

# Bull Queue
BULL_PREFIX=parkfan

External APIs

# 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

# 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

Sync & Processing

# 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

🀝 Contributing

This is a private project. For questions or collaboration inquiries, please contact the maintainer.


πŸ“„ License

UNLICENSED β€” Private project by Patrick Arns


πŸ™ Powered By

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

API Documentation Β· Frontend

About

api.park.fan

Resources

Stars

Watchers

Forks

Contributors