Basic data ingestion and analytics
This commit is contained in:
871
API-DOCUMENTATION.md
Normal file
871
API-DOCUMENTATION.md
Normal file
@@ -0,0 +1,871 @@
|
||||
# GTFS API Documentation
|
||||
|
||||
Comprehensive API documentation for the Skopje Bus Tracker GTFS-based API.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [OpenAPI Specification](#openapi-specification)
|
||||
- [Base URL](#base-url)
|
||||
- [GTFS Static Data Endpoints](#gtfs-static-data-endpoints)
|
||||
- [Real-Time Data Endpoints](#real-time-data-endpoints)
|
||||
- [Historical Data Endpoints](#historical-data-endpoints)
|
||||
- [Statistics & Analytics Endpoints](#statistics--analytics-endpoints)
|
||||
- [Data Models](#data-models)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Rate Limiting](#rate-limiting)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This API provides access to Skopje public transport data based on the **GTFS (General Transit Feed Specification)** standard. It combines static GTFS data with real-time information and historical analytics stored in TimescaleDB.
|
||||
|
||||
### Key Features
|
||||
- **GTFS-compliant** static data (stops, routes)
|
||||
- **Real-time** vehicle positions and arrival predictions
|
||||
- **Historical** time-series data for analytics
|
||||
- **Statistical** analysis of delays and patterns
|
||||
|
||||
### Upstream API
|
||||
|
||||
This application consumes data from the **ModeShift GTFS API**. For documentation on the upstream API endpoints:
|
||||
- See **[UPSTREAM-API-DOCUMENTATION.md](UPSTREAM-API-DOCUMENTATION.md)**
|
||||
|
||||
---
|
||||
|
||||
## OpenAPI Specification
|
||||
|
||||
A complete **OpenAPI 3.0** specification is available at [openapi.yaml](openapi.yaml). This machine-readable specification includes:
|
||||
|
||||
- All API endpoints with parameters and responses
|
||||
- Request/response schemas and data models
|
||||
- Examples for all operations
|
||||
- Error response formats
|
||||
|
||||
### Using the OpenAPI Spec
|
||||
|
||||
**Interactive Documentation (Local):**
|
||||
|
||||
Once the server is running, visit:
|
||||
```
|
||||
http://localhost:3000/api-docs.html
|
||||
```
|
||||
|
||||
This provides an interactive Swagger UI where you can:
|
||||
- Browse all API endpoints
|
||||
- View request/response schemas
|
||||
- Try out API calls directly from the browser
|
||||
- Download the OpenAPI specification
|
||||
|
||||
**Interactive Documentation with Swagger UI (Alternative):**
|
||||
```bash
|
||||
# Using Docker
|
||||
docker run -p 8080:8080 -e SWAGGER_JSON=/openapi.yaml -v $(pwd)/openapi.yaml:/openapi.yaml swaggerapi/swagger-ui
|
||||
|
||||
# Or use online editor
|
||||
# Visit: https://editor.swagger.io/
|
||||
# Then paste the contents of openapi.yaml
|
||||
```
|
||||
|
||||
**Generate Client Libraries:**
|
||||
```bash
|
||||
# Install OpenAPI Generator
|
||||
npm install -g @openapitools/openapi-generator-cli
|
||||
|
||||
# Generate TypeScript client
|
||||
openapi-generator-cli generate -i openapi.yaml -g typescript-fetch -o ./client/typescript
|
||||
|
||||
# Generate Python client
|
||||
openapi-generator-cli generate -i openapi.yaml -g python -o ./client/python
|
||||
|
||||
# Generate Go client
|
||||
openapi-generator-cli generate -i openapi.yaml -g go -o ./client/go
|
||||
```
|
||||
|
||||
**VS Code Integration:**
|
||||
Install the "OpenAPI (Swagger) Editor" extension to view and edit the spec with validation and IntelliSense.
|
||||
|
||||
---
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
For production, replace with your deployed server URL.
|
||||
|
||||
---
|
||||
|
||||
## GTFS Static Data Endpoints
|
||||
|
||||
### Get Configuration
|
||||
|
||||
Retrieve configuration including default stop and route information.
|
||||
|
||||
**Endpoint:** `GET /api/config`
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `stopId` | string | No | Specific stop ID to query (defaults to configured default) |
|
||||
| `routeId` | string | No | Specific route ID to query (defaults to configured default) |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"stop": {
|
||||
"id": "1563",
|
||||
"code": "363",
|
||||
"name": "KARPOS II - STIV NAUMOV",
|
||||
"lat": 41.97964,
|
||||
"lon": 21.41283
|
||||
},
|
||||
"route": {
|
||||
"id": "125",
|
||||
"shortName": "7",
|
||||
"longName": "Centar - GTC - Karpos 4 - Karposh 2"
|
||||
},
|
||||
"defaults": {
|
||||
"stopId": "1563",
|
||||
"routeId": "125"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/config?stopId=1571&routeId=125"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get All Stops
|
||||
|
||||
Retrieve all GTFS stops in the system.
|
||||
|
||||
**Endpoint:** `GET /api/stops`
|
||||
|
||||
**Query Parameters:** None
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "1563",
|
||||
"code": "363",
|
||||
"name": "KARPOS II - STIV NAUMOV",
|
||||
"lat": 41.97964,
|
||||
"lon": 21.41283
|
||||
},
|
||||
{
|
||||
"id": "1571",
|
||||
"code": "371",
|
||||
"name": "AMERICAN COLLEGE - VASIL GLAVINOV",
|
||||
"lat": 42.00437,
|
||||
"lon": 21.39687
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/stops"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Populate stop selection dropdowns
|
||||
- Build stop search functionality
|
||||
- Generate maps with all stops
|
||||
|
||||
---
|
||||
|
||||
### Get All Routes
|
||||
|
||||
Retrieve all GTFS routes in the system.
|
||||
|
||||
**Endpoint:** `GET /api/routes`
|
||||
|
||||
**Query Parameters:** None
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "125",
|
||||
"shortName": "7",
|
||||
"longName": "Centar - GTC - Karpos 4 - Karposh 2"
|
||||
},
|
||||
{
|
||||
"id": "128",
|
||||
"shortName": "2",
|
||||
"longName": "Kisela Voda - Centar - 11 Oktomvri - Avtokomanda"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/routes"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Display available routes
|
||||
- Filter by route number
|
||||
- Route selection interfaces
|
||||
|
||||
---
|
||||
|
||||
## Real-Time Data Endpoints
|
||||
|
||||
### Get Real-Time Arrivals
|
||||
|
||||
Get upcoming bus arrivals at a specific stop with real-time predictions.
|
||||
|
||||
**Endpoint:** `GET /api/arrivals`
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `stopId` | string | No | Stop ID to query (defaults to configured default) |
|
||||
| `routeId` | string | No | Filter by specific route ID (defaults to configured default) |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"arrivalTime": "2026-02-07T14:35:00.000Z",
|
||||
"scheduledTime": "2026-02-07T14:33:00.000Z",
|
||||
"minutesUntil": 8,
|
||||
"delaySeconds": 120,
|
||||
"headsign": "Karpos 2",
|
||||
"isRealtime": true,
|
||||
"realtimeState": "UPDATED"
|
||||
},
|
||||
{
|
||||
"arrivalTime": "2026-02-07T14:48:00.000Z",
|
||||
"scheduledTime": "2026-02-07T14:48:00.000Z",
|
||||
"minutesUntil": 21,
|
||||
"delaySeconds": 0,
|
||||
"headsign": "Centar",
|
||||
"isRealtime": true,
|
||||
"realtimeState": "SCHEDULED"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
- `arrivalTime`: Predicted arrival time (ISO 8601)
|
||||
- `scheduledTime`: Originally scheduled time (ISO 8601)
|
||||
- `minutesUntil`: Minutes until arrival (can be negative if bus passed)
|
||||
- `delaySeconds`: Delay in seconds (positive = late, negative = early)
|
||||
- `headsign`: Destination shown on bus
|
||||
- `isRealtime`: Whether prediction is based on real-time data
|
||||
- `realtimeState`: `SCHEDULED`, `UPDATED`, or `CANCELED`
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/arrivals?stopId=1563&routeId=125"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Returns arrivals from 2 minutes ago up to configured minutes ahead
|
||||
- Results are sorted by arrival time
|
||||
- Data is also logged to database for historical analysis
|
||||
|
||||
---
|
||||
|
||||
### Get Real-Time Vehicle Positions
|
||||
|
||||
Get current positions of all vehicles on a specific route.
|
||||
|
||||
**Endpoint:** `GET /api/vehicles`
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `routeId` | string | No | Route ID to filter vehicles (defaults to configured default) |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 12345,
|
||||
"vehicleId": "MK-SK-1234",
|
||||
"label": "1234",
|
||||
"lat": 41.9981,
|
||||
"lon": 21.4254,
|
||||
"bearing": 87.5,
|
||||
"speed": 15.3,
|
||||
"timestamp": "2026-02-07T14:27:15Z",
|
||||
"tripId": "trip_789",
|
||||
"currentStopSequence": 0,
|
||||
"currentStatus": "IN_TRANSIT_TO"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
- `id`: Internal vehicle database ID
|
||||
- `vehicleId`: Vehicle identification number (license plate)
|
||||
- `label`: Bus inventory number (shown on bus)
|
||||
- `lat`/`lon`: Current GPS coordinates
|
||||
- `bearing`: Direction of travel (0-360 degrees, 0=North)
|
||||
- `speed`: Current speed in km/h
|
||||
- `timestamp`: Last position update time
|
||||
- `tripId`: GTFS trip ID vehicle is currently serving
|
||||
- `currentStatus`: `IN_TRANSIT_TO`, `STOPPED_AT`, or `UNKNOWN`
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/vehicles?routeId=125"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Combines GTFS-RT trip updates with JSON vehicle API
|
||||
- Vehicle positions are logged to database
|
||||
- Updates typically every 10-30 seconds from source
|
||||
|
||||
---
|
||||
|
||||
## Historical Data Endpoints
|
||||
|
||||
### Get Database Statistics
|
||||
|
||||
Get overview statistics about stored historical data.
|
||||
|
||||
**Endpoint:** `GET /api/stats/db`
|
||||
|
||||
**Query Parameters:** None
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"vehiclePositions": {
|
||||
"totalRecords": 1523847,
|
||||
"oldestRecord": "2026-01-15T08:00:00.000Z",
|
||||
"newestRecord": "2026-02-07T14:27:30.000Z",
|
||||
"dataRetentionDays": 90
|
||||
},
|
||||
"arrivalRecords": {
|
||||
"totalRecords": 456231,
|
||||
"oldestRecord": "2026-01-15T08:00:00.000Z",
|
||||
"newestRecord": "2026-02-07T14:27:30.000Z",
|
||||
"dataRetentionDays": 90
|
||||
},
|
||||
"databaseSize": "2.3 GB",
|
||||
"status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/stats/db"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Get Vehicle Position History
|
||||
|
||||
Retrieve historical GPS positions for a specific vehicle.
|
||||
|
||||
**Endpoint:** `GET /api/history/vehicle/:vehicleId`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `vehicleId` | string | Yes | Vehicle identification number or label |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `hours` | integer | No | 24 | Number of hours to look back |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"time": "2026-02-07T10:00:00.000Z",
|
||||
"vehicleId": "MK-SK-1234",
|
||||
"routeId": "125",
|
||||
"tripId": "trip_789",
|
||||
"latitude": 41.9981,
|
||||
"longitude": 21.4254,
|
||||
"speed": 15.3,
|
||||
"bearing": 87.5,
|
||||
"currentStatus": "IN_TRANSIT_TO"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/history/vehicle/1234?hours=12"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Visualize vehicle path on map
|
||||
- Analyze driving patterns
|
||||
- Calculate actual travel times
|
||||
|
||||
---
|
||||
|
||||
### Get Route Vehicle History
|
||||
|
||||
Get historical positions of all vehicles that served a specific route.
|
||||
|
||||
**Endpoint:** `GET /api/history/route/:routeId/vehicles`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `routeId` | string | Yes | GTFS route ID |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `hours` | integer | No | 24 | Number of hours to look back |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"time": "2026-02-07T10:00:00.000Z",
|
||||
"vehicleId": "1234",
|
||||
"routeId": "125",
|
||||
"tripId": "trip_789",
|
||||
"latitude": 41.9981,
|
||||
"longitude": 21.4254,
|
||||
"speed": 15.3,
|
||||
"bearing": 87.5
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/history/route/125/vehicles?hours=6"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Service frequency analysis
|
||||
- Route coverage visualization
|
||||
- Fleet utilization tracking
|
||||
|
||||
---
|
||||
|
||||
### Get Stop Arrival History
|
||||
|
||||
Retrieve historical arrival records for a specific stop.
|
||||
|
||||
**Endpoint:** `GET /api/history/stop/:stopId/arrivals`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `stopId` | string | Yes | GTFS stop ID |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `routeId` | string | No | (all) | Filter by specific route |
|
||||
| `hours` | integer | No | 24 | Number of hours to look back |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"time": "2026-02-07T10:15:00.000Z",
|
||||
"stopId": "1563",
|
||||
"routeId": "125",
|
||||
"scheduledTime": "2026-02-07T10:15:00.000Z",
|
||||
"predictedTime": "2026-02-07T10:17:30.000Z",
|
||||
"delaySeconds": 150,
|
||||
"isRealtime": true,
|
||||
"headsign": "Karpos 2"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/history/stop/1563/arrivals?routeId=125&hours=48"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Statistics & Analytics Endpoints
|
||||
|
||||
### Get Route Delay Statistics
|
||||
|
||||
Get delay statistics and performance metrics for a route.
|
||||
|
||||
**Endpoint:** `GET /api/stats/route/:routeId/delays`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `routeId` | string | Yes | GTFS route ID |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `hours` | integer | No | 24 | Time window for analysis |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"routeId": "125",
|
||||
"periodHours": 24,
|
||||
"statistics": {
|
||||
"totalArrivals": 287,
|
||||
"avgDelaySeconds": 62,
|
||||
"medianDelaySeconds": 45,
|
||||
"minDelaySeconds": -30,
|
||||
"maxDelaySeconds": 420,
|
||||
"stdDeviation": 78.5,
|
||||
"onTimePercentage": 68.3,
|
||||
"earlyPercentage": 8.7,
|
||||
"latePercentage": 23.0,
|
||||
"veryLatePercentage": 4.2
|
||||
},
|
||||
"thresholds": {
|
||||
"onTime": "±5 minutes",
|
||||
"veryLate": ">10 minutes"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/stats/route/125/delays?hours=72"
|
||||
```
|
||||
|
||||
**Definitions:**
|
||||
- **On-time**: Within ±5 minutes of schedule
|
||||
- **Early**: More than 5 minutes early
|
||||
- **Late**: 5-10 minutes late
|
||||
- **Very Late**: More than 10 minutes late
|
||||
|
||||
---
|
||||
|
||||
### Get Stop Delay Statistics
|
||||
|
||||
Get delay statistics for a specific stop.
|
||||
|
||||
**Endpoint:** `GET /api/stats/stop/:stopId/delays`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `stopId` | string | Yes | GTFS stop ID |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `hours` | integer | No | 24 | Time window for analysis |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"stopId": "1563",
|
||||
"periodHours": 24,
|
||||
"statistics": {
|
||||
"totalArrivals": 145,
|
||||
"avgDelaySeconds": 72,
|
||||
"medianDelaySeconds": 60,
|
||||
"onTimePercentage": 62.1,
|
||||
"routeBreakdown": [
|
||||
{
|
||||
"routeId": "125",
|
||||
"routeName": "7",
|
||||
"arrivals": 89,
|
||||
"avgDelaySeconds": 65
|
||||
},
|
||||
{
|
||||
"routeId": "128",
|
||||
"routeName": "2",
|
||||
"arrivals": 56,
|
||||
"avgDelaySeconds": 82
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/stats/stop/1563/delays?hours=48"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Compare reliability across stops
|
||||
- Identify problematic locations
|
||||
- Plan transfer reliability
|
||||
|
||||
---
|
||||
|
||||
### Get Route Hourly Pattern
|
||||
|
||||
Analyze delay patterns by hour of day for a route.
|
||||
|
||||
**Endpoint:** `GET /api/stats/route/:routeId/hourly`
|
||||
|
||||
**Path Parameters:**
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `routeId` | string | Yes | GTFS route ID |
|
||||
|
||||
**Query Parameters:**
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `days` | integer | No | 7 | Number of days to analyze |
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"routeId": "125",
|
||||
"periodDays": 7,
|
||||
"hourlyPattern": [
|
||||
{
|
||||
"hour": 6,
|
||||
"arrivals": 42,
|
||||
"avgDelaySeconds": 15,
|
||||
"onTimePercentage": 85.7
|
||||
},
|
||||
{
|
||||
"hour": 7,
|
||||
"arrivals": 89,
|
||||
"avgDelaySeconds": 45,
|
||||
"onTimePercentage": 71.9
|
||||
},
|
||||
{
|
||||
"hour": 8,
|
||||
"arrivals": 124,
|
||||
"avgDelaySeconds": 125,
|
||||
"onTimePercentage": 52.4
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/stats/route/125/hourly?days=14"
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Identify rush hour patterns
|
||||
- Compare morning vs evening reliability
|
||||
- Optimize schedules based on actual performance
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### GTFS Stop
|
||||
```typescript
|
||||
interface GtfsStop {
|
||||
stop_id: string; // Unique stop identifier
|
||||
stop_code: string; // Public-facing stop code
|
||||
stop_name: string; // Stop name
|
||||
stop_lat: number; // Latitude (WGS84)
|
||||
stop_lon: number; // Longitude (WGS84)
|
||||
}
|
||||
```
|
||||
|
||||
### GTFS Route
|
||||
```typescript
|
||||
interface GtfsRoute {
|
||||
route_id: string; // Unique route identifier
|
||||
route_short_name: string; // Public route number (e.g., "7")
|
||||
route_long_name: string; // Full route name
|
||||
route_type?: string; // GTFS route type (0=Tram, 3=Bus, etc.)
|
||||
}
|
||||
```
|
||||
|
||||
### Vehicle Position
|
||||
```typescript
|
||||
interface VehiclePosition {
|
||||
time: Date; // Timestamp (ISO 8601)
|
||||
vehicle_id: string; // Vehicle identifier
|
||||
route_id: string; // Route being served
|
||||
trip_id?: string; // Current trip ID
|
||||
latitude: number; // GPS latitude
|
||||
longitude: number; // GPS longitude
|
||||
speed?: number; // Speed in km/h
|
||||
bearing?: number; // Direction (0-360°)
|
||||
current_status: string; // IN_TRANSIT_TO, STOPPED_AT, UNKNOWN
|
||||
}
|
||||
```
|
||||
|
||||
### Arrival Record
|
||||
```typescript
|
||||
interface ArrivalRecord {
|
||||
time: Date; // Record timestamp
|
||||
stop_id: string; // Stop ID
|
||||
route_id: string; // Route ID
|
||||
scheduled_time: Date; // Scheduled arrival time
|
||||
predicted_time?: Date; // Real-time predicted time
|
||||
actual_time?: Date; // Actual arrival time (if observed)
|
||||
delay_seconds: number; // Delay in seconds
|
||||
is_realtime: boolean; // Based on real-time data
|
||||
headsign?: string; // Trip headsign
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
All endpoints return standard HTTP status codes:
|
||||
|
||||
| Status Code | Description |
|
||||
|-------------|-------------|
|
||||
| `200` | Success |
|
||||
| `404` | Resource not found (e.g., invalid stop/route ID) |
|
||||
| `500` | Server error |
|
||||
|
||||
**Error Response Format:**
|
||||
```json
|
||||
{
|
||||
"error": "Description of the error",
|
||||
"details": "Additional error details (when available)"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Error:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/config?stopId=invalid"
|
||||
```
|
||||
|
||||
Response (404):
|
||||
```json
|
||||
{
|
||||
"error": "Stop invalid not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Currently, there is no rate limiting implemented. For production deployments, consider:
|
||||
|
||||
- Implementing per-IP rate limits
|
||||
- Using caching for frequently accessed endpoints
|
||||
- Load balancing for high-traffic scenarios
|
||||
|
||||
**Recommended Polling Intervals:**
|
||||
- Real-time arrivals: 10-30 seconds
|
||||
- Vehicle positions: 10-30 seconds
|
||||
- Historical data: As needed (not time-sensitive)
|
||||
- Statistics: 5-60 minutes (depending on use case)
|
||||
|
||||
---
|
||||
|
||||
## GTFS Data Update
|
||||
|
||||
The GTFS static data (stops, routes, schedules) is loaded from the `gtfs/` directory on server startup.
|
||||
|
||||
**To update GTFS data:**
|
||||
|
||||
```bash
|
||||
bun run setup-gtfs.ts
|
||||
```
|
||||
|
||||
This downloads the latest GTFS feed and extracts it. Restart the server to load the new data.
|
||||
|
||||
---
|
||||
|
||||
## TimescaleDB Setup
|
||||
|
||||
Historical data and analytics require TimescaleDB. See the [README.md](README.md) for setup instructions.
|
||||
|
||||
**Quick start:**
|
||||
```bash
|
||||
cd infrastructure
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The database automatically:
|
||||
- Creates hypertables for time-series data
|
||||
- Sets up 90-day data retention
|
||||
- Builds continuous aggregates for hourly metrics
|
||||
- Maintains indexes for fast queries
|
||||
|
||||
---
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### Building a Real-Time Tracker
|
||||
|
||||
```javascript
|
||||
// 1. Get all stops
|
||||
const stops = await fetch('/api/stops').then(r => r.json());
|
||||
|
||||
// 2. Get all routes
|
||||
const routes = await fetch('/api/routes').then(r => r.json());
|
||||
|
||||
// 3. Poll for arrivals every 15 seconds
|
||||
setInterval(async () => {
|
||||
const arrivals = await fetch('/api/arrivals?stopId=1563&routeId=125')
|
||||
.then(r => r.json());
|
||||
updateUI(arrivals);
|
||||
}, 15000);
|
||||
|
||||
// 4. Poll for vehicle positions
|
||||
setInterval(async () => {
|
||||
const vehicles = await fetch('/api/vehicles?routeId=125')
|
||||
.then(r => r.json());
|
||||
updateMap(vehicles);
|
||||
}, 15000);
|
||||
```
|
||||
|
||||
### Building Analytics Dashboard
|
||||
|
||||
```javascript
|
||||
// Get delay statistics for route
|
||||
const stats = await fetch('/api/stats/route/125/delays?hours=168')
|
||||
.then(r => r.json());
|
||||
|
||||
// Get hourly patterns
|
||||
const pattern = await fetch('/api/stats/route/125/hourly?days=30')
|
||||
.then(r => r.json());
|
||||
|
||||
// Get stop performance comparison
|
||||
const stopStats = await fetch('/api/stats/stop/1563/delays?hours=168')
|
||||
.then(r => r.json());
|
||||
|
||||
// Display charts with the data
|
||||
renderDelayChart(stats);
|
||||
renderHourlyPattern(pattern);
|
||||
renderStopComparison(stopStats);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **OpenAPI Specification**: [openapi.yaml](openapi.yaml) - Machine-readable API spec
|
||||
- **Upstream API Documentation**: [UPSTREAM-API-DOCUMENTATION.md](UPSTREAM-API-DOCUMENTATION.md) - ModeShift GTFS API docs
|
||||
- **Swagger UI Demo**: https://editor.swagger.io/ (paste openapi.yaml content)
|
||||
- **GTFS Specification**: https://gtfs.org/
|
||||
- **GTFS-RT Reference**: https://gtfs.org/realtime/
|
||||
- **TimescaleDB Documentation**: https://docs.timescale.com/
|
||||
- **Project README**: [README.md](README.md)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check the [README.md](README.md) for setup instructions
|
||||
2. Review this documentation
|
||||
3. Check the source code in [server.ts](server.ts) and [lib/gtfs.ts](lib/gtfs.ts)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** February 7, 2026
|
||||
**API Version:** 1.0.0
|
||||
Reference in New Issue
Block a user