🎒 Park.Fan API

The ultimate REST API for theme park data, ride information, and real-time queue times! πŸš€

Built with NestJS and TypeScript - a high-performance, modern API providing comprehensive access to detailed information about theme parks worldwide, including their attractions and current wait times.

✨ Features - What Makes This API Awesome

πŸ“Š Data Source

This API integrates with queue-times.com to provide reliable and up-to-date information about theme park wait times and attraction data from around the world.

🌐 Live API

Experience the API live at https://api.park.fan - test it with real theme park data and interactive documentation! 🎯

πŸš€ Quick Start - How to Get Going Fast!

Prerequisites

Installation & Setup

# Clone the repository
git clone <repository-url>
cd api.park.fan

# Install dependencies (pnpm is super fast!)
pnpm install

# Configure environment
cp .env.example .env
# Edit .env with your database credentials

Database Setup - It Couldn't Be Easier!

The API creates the database automatically! Just ensure PostgreSQL is running:

# The application automatically:
# 1. πŸ”Œ Connects to PostgreSQL
# 2. πŸ—οΈ Creates database if it doesn't exist
# 3. πŸš€ Executes migrations automatically
# 4. πŸ“‘ Starts data synchronization

Starting the API - Let's Go! πŸš€

# Development Mode with Hot Reload (for development)
pnpm run start:dev

# Production Build and Start
pnpm run build
pnpm run start:prod

# Debug Mode (for troubleshooting)
pnpm run start:debug

🎯 API Ready! Go to http://localhost:3000 for interactive documentation

βš™οΈ Configuration - Make It Your Own!

Configure the API using environment variables in your .env file:

Important Environment Variables

Variable Description Default Required
DB_HOST PostgreSQL Database Host localhost βœ…
DB_PORT PostgreSQL Database Port 5432 βœ…
DB_USER PostgreSQL Username postgres βœ…
DB_PASS PostgreSQL Password postgres βœ…
DB_NAME PostgreSQL Database Name parkfan βœ…
PARK_OPEN_THRESHOLD_PERCENT Park "open" threshold (0-100%) 50 ❌

🎯 Core Features

🏰 Park Operating Status Logic

The Park Operating Status feature intelligently determines whether a park is "open" or "closed":

Examples:

# Use standard 50% threshold
GET /statistics

# Custom 75% threshold for stricter "open" definition
GET /statistics?openThreshold=75

# Relaxed 25% threshold
GET /parks?openThreshold=25

🌑️ Crowd Level Intelligence

The Crowd Level feature provides intelligent real-time park congestion analysis:

Crowd Level Scale:

Configuration:

# Include crowd level (default)
GET /parks?includeCrowdLevel=true

# Skip crowd level for faster response
GET /parks?includeCrowdLevel=false

🌀️ Live Weather Data

Provides current weather conditions and 7-day forecasts for each park location:

Configuration:

# Include weather data (default)
GET /parks?includeWeather=true

# Skip weather data for faster response
GET /parks?includeWeather=false

Data Source: Powered by Open-Meteo API with global coverage and WMO standard weather codes.

🎯 API Endpoints - Where the Magic Happens!

🏠 Home & Documentation

Method Endpoint Description
GET / πŸ“– Interactive API documentation (HTML) - Beautifully formatted!
GET /readme πŸ“„ Raw documentation (Markdown)
GET /openapi.yaml πŸ“‹ OpenAPI 3.0.3 specification (YAML)

πŸ’‘ Pro Tip: Import the OpenAPI specification into tools like Postman, Insomnia, or Swagger Editor for interactive API testing!

🏰 Parks - The Theme Parks!

Method Endpoint Description
GET /parks 🌟 All parks with advanced filters & pagination
GET /parks/:id 🎯 Specific park with all ride details
GET /parks/:id/rides 🎠 All rides for a specific park

πŸ—ΊοΈ Hierarchical Routes - Navigate by Location!

Method Endpoint Description
GET /parks/:continent 🌍 All parks in a continent
GET /parks/:continent/:country πŸ‡©πŸ‡ͺ All parks in a country
GET /parks/:continent/:country/:park 🏰 Access park via hierarchical path
GET /parks/:continent/:country/:park/:ride 🎒 Access ride via hierarchical path

Smart Routing:

URL Transformation Rules:

Examples:

🎠 Rides - The Attractions!

Method Endpoint Description
GET /rides πŸ” All rides with filtering & search
GET /rides/:id 🎯 Specific ride with current queue status

πŸ“Š Statistics & Analytics - The Insights!

Method Endpoint Description
GET /statistics πŸ“ˆ Comprehensive statistics with geographic breakdowns

🌍 Geographic Data

Method Endpoint Description
GET /countries πŸ‡©πŸ‡ͺ All countries with park counts
GET /continents 🌎 All continents with park counts

⚑ System Status

Method Endpoint Description
GET /status πŸ’š API health check and system information

πŸ” Query Parameters & Filtering - Find Exactly What You Want!

🏰 Parks Filtering (/parks)

Parameter Type Description Example
search string Search by park name or country ?search=Disney
country string Filter by specific country ?country=Germany
continent string Filter by continent ?continent=Europe
parkGroupId number Filter by park group ?parkGroupId=1
openThreshold number Operational status threshold (0-100) ?openThreshold=75
includeCrowdLevel boolean Include crowd level calculation ?includeCrowdLevel=false
includeWeather boolean Include live weather data ?includeWeather=false
page number Page number (β‰₯1) ?page=2
limit number Results per page (max 100) ?limit=20

🎠 Rides Filtering (/rides)

Parameter Type Description Example
search string Search by ride name ?search=coaster
parkId number Filter by specific park ?parkId=25
isActive boolean Filter by operational status ?isActive=true
page number Page number (β‰₯1) ?page=3
limit number Results per page (max 100) ?limit=50

πŸ“Š Statistics Parameters (/statistics)

Parameter Type Description Example
openThreshold number Park operational threshold (0-100) ?openThreshold=60

πŸš€ Example API Calls

πŸ” Search & Filter Parks

# Find Disney parks worldwide
GET https://api.park.fan/parks?search=Disney&limit=10

# All German parks
GET https://api.park.fan/parks?country=Germany

# European parks with relaxed "open" criteria
GET https://api.park.fan/parks?continent=Europe&openThreshold=25

# Parks with crowd level analysis (default)
GET https://api.park.fan/parks?search=Disney&includeCrowdLevel=true

# Fast response without crowd level calculation
GET https://api.park.fan/parks?country=Germany&includeCrowdLevel=false

# Fast response without weather data
GET https://api.park.fan/parks?country=Germany&includeWeather=false

# Complete data with both crowd level and weather (default)
GET https://api.park.fan/parks?search=Disney

# Minimal response - no crowd level or weather for maximum speed
GET https://api.park.fan/parks?includeCrowdLevel=false&includeWeather=false

# Parks in a specific group with pagination
GET https://api.park.fan/parks?parkGroupId=1&page=2&limit=5

πŸ—ΊοΈ Hierarchical Navigation

# Get all parks in Europe
GET https://api.park.fan/parks/europe

# Get all parks in Germany
GET https://api.park.fan/parks/europe/germany

# Access Phantasialand via hierarchical path
GET https://api.park.fan/parks/europe/germany/phantasialand

# Access specific ride via hierarchical path
GET https://api.park.fan/parks/europe/germany/phantasialand/taron

# Access parks with complex names
GET https://api.park.fan/parks/north-america/united-states/islands-of-adventure-at-universal-orlando

# Access European park
GET https://api.park.fan/parks/europe/england/alton-towers/the-smiler

# Backward compatibility - access by ID
GET https://api.park.fan/parks/61

🎒 Discover Rides

# Search for roller coasters
GET https://api.park.fan/rides?search=coaster&limit=20

# All rides at Disneyland Paris
GET https://api.park.fan/parks/26/rides

# Active rides only with pagination
GET https://api.park.fan/rides?isActive=true&page=1&limit=25

πŸ“Š Analytics & Statistics

# Global theme park statistics
GET https://api.park.fan/statistics

# Statistics with strict "open" criteria (75%)
GET https://api.park.fan/statistics?openThreshold=75

🌍 Geographic Exploration

# All countries with parks
GET https://api.park.fan/countries

# Continental breakdown
GET https://api.park.fan/continents

πŸ“‹ Response Examples

🏰 Park Details with Operating Status & Weather

GET https://api.park.fan/parks/25
{
  "id": 25,
  "name": "Disneyland Park",
  "country": "United States",
  "continent": "North America",
  "timezone": "America/Los_Angeles",
  "latitude": 33.8121,
  "longitude": -117.919,
  "isActive": true,
  "operatingStatus": {
    "isOpen": true,
    "openRideCount": 42,
    "totalRideCount": 58,
    "operatingPercentage": 72.4,
    "openThreshold": 50
  },
  "weather": {
    "current": {
      "temperature": {
        "min": 22,
        "max": 28
      },
      "precipitationProbability": 10,
      "weatherCode": 1,
      "status": "partly_cloudy",
      "weatherScore": 92
    },
    "forecast": [
      {
        "date": "2025-06-22",
        "temperature": {
          "min": 20,
          "max": 30
        },
        "precipitationProbability": 5,
        "weatherCode": 0,
        "status": "sunny",
        "weatherScore": 95
      }
    ]
  },
  "crowdLevel": {
    "level": 85,
    "label": "Moderate",
    "ridesUsed": 15,
    "totalRides": 42,
    "historicalBaseline": 35,
    "currentAverage": 42,
    "confidence": 82,
    "calculatedAt": "2025-06-20T15:30:00Z"
  },
  "themeAreas": [
    {
      "id": 123,
      "name": "Fantasyland",
      "rides": [...]
    }
  ]
}

πŸ—ΊοΈ Hierarchical Park Access

GET https://api.park.fan/parks/europe/germany/phantasialand
{
  "id": 61,
  "name": "Phantasialand",
  "country": "Germany",
  "continent": "Europe",
  "timezone": "Europe/Berlin",
  "latitude": 50.7998,
  "longitude": 6.8783,
  "isActive": true,
  "hierarchicalUrl": "/parks/europe/germany/phantasialand",
  "operatingStatus": {
    "isOpen": true,
    "openRideCount": 8,
    "totalRideCount": 12,
    "operatingPercentage": 66.7,
    "openThreshold": 50
  },
  "themeAreas": [
    {
      "id": 234,
      "name": "Klugheim",
      "rides": [
        {
          "id": 456,
          "name": "Taron",
          "hierarchicalUrl": "/parks/europe/germany/phantasialand/taron",
          "isActive": true,
          "waitTime": 45,
          "lastUpdate": "2025-06-20T15:28:00Z"
        }
      ]
    }
  ]
}

🎒 Ride with Queue Information

GET https://api.park.fan/rides/456
{
  "id": 456,
  "name": "Taron",
  "hierarchicalUrl": "/parks/europe/germany/phantasialand/taron",
  "park": {
    "id": 61,
    "name": "Phantasialand",
    "hierarchicalUrl": "/parks/europe/germany/phantasialand"
  },
  "themeArea": {
    "id": 234,
    "name": "Klugheim"
  },
  "isActive": true,
  "waitTime": 45,
  "lastUpdate": "2025-06-20T15:28:00Z"
}

πŸ“Š Statistics Overview

GET https://api.park.fan/statistics
{
  "global": {
    "totalParks": 142,
    "totalRides": 3247,
    "openParks": 89,
    "closedParks": 53,
    "openPercentage": 62.7,
    "openThreshold": 50
  },
  "longestWaitTimes": [
    {
      "park": {
        "id": 25,
        "name": "Disneyland Park",
        "hierarchicalUrl": "/parks/north-america/united-states/disneyland-park"
      },
      "ride": {
        "id": 789,
        "name": "Space Mountain",
        "hierarchicalUrl": "/parks/north-america/united-states/disneyland-park/space-mountain"
      },
      "waitTime": 120,
      "lastUpdate": "2025-06-20T15:30:00Z"
    }
  ],
  "shortestWaitTimes": [
    {
      "park": {
        "id": 61,
        "name": "Phantasialand",
        "hierarchicalUrl": "/parks/europe/germany/phantasialand"
      },
      "ride": {
        "id": 456,
        "name": "Taron",
        "hierarchicalUrl": "/parks/europe/germany/phantasialand/taron"
      },
      "waitTime": 5,
      "lastUpdate": "2025-06-20T15:25:00Z"
    }
  ],
  "busiestParks": [
    {
      "id": 25,
      "name": "Disneyland Park",
      "hierarchicalUrl": "/parks/north-america/united-states/disneyland-park",
      "averageWaitTime": 75,
      "operatingPercentage": 72.4
    }
  ],
  "quietestParks": [
    {
      "id": 61,
      "name": "Phantasialand",
      "hierarchicalUrl": "/parks/europe/germany/phantasialand",
      "averageWaitTime": 18,
      "operatingPercentage": 66.7
    }
  ],
  "byContinent": {
    "North America": {
      "totalParks": 45,
      "openParks": 28,
      "openPercentage": 62.2
    },
    "Europe": {
      "totalParks": 67,
      "openParks": 42,
      "openPercentage": 62.7
    },
    "Asia": {
      "totalParks": 30,
      "openParks": 19,
      "openPercentage": 63.3
    }
  }
}

πŸ› οΈ Development & Architecture

πŸ“ Project Structure

src/
β”œβ”€β”€ main.ts                    # Application entry point
β”œβ”€β”€ app.module.ts             # Root application module
└── modules/
    β”œβ”€β”€ parks/                # Parks module with weather integration
    β”œβ”€β”€ rides/                # Rides management
    β”œβ”€β”€ statistics/           # Analytics and statistics
    β”œβ”€β”€ countries/            # Country data
    β”œβ”€β”€ continents/           # Continental data
    β”œβ”€β”€ database/             # Database configuration
    β”œβ”€β”€ queue-times-parser/   # External data synchronization
    └── utils/                # Shared utilities and services

πŸ”§ Technology Stack

πŸš€ Deployment

The API is production-ready and designed for horizontal scaling:

πŸ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“ž Support

For questions or support, please contact:


Built with ❀️ for theme park enthusiasts worldwide 🎒