A real-time GraphQL API for tracking Grand River Transit (GRT) buses, built with Kotlin, Spring Boot, and enriched with static GTFS data.
- GraphQL API: Query trips, vehicle positions, routes, and stops
- Enriched Data: Route names, stop names, and trip headsigns joined from static GTFS data — not just raw IDs
- Real-time Updates: Subscribe to live trip updates via GraphQL Subscriptions (WebSockets)
- Vehicle Tracking: Live vehicle positions with lat/lon, bearing, and speed
- Automatic Polling: Fetches data from GTFS Realtime feeds every 30 seconds
- Static GTFS Loader: Downloads and loads GRT's
google_transit.zipon startup into an embedded H2 database - Zero Infrastructure: No Kafka, no Docker — just run the JAR
- Java 21 or higher
./gradlew bootRunThe server will start on http://localhost:8080. On startup, it will:
- Download and parse GRT's static GTFS data (routes, stops, trips)
- Begin polling realtime trip updates and vehicle positions every 30 seconds
Open the GraphiQL interface in your browser:
http://localhost:8080/graphiql.html
Get all trips with enriched data:
query {
trips {
tripId
routeId
routeName
tripHeadsign
vehicle {
id
label
}
stopTimeUpdates {
stopId
stopName
arrival {
delay
}
}
}
}Filter trips by route:
query {
trips(routeId: "7") {
tripId
routeName
vehicle { label }
}
}Get live vehicle positions:
query {
vehiclePositions {
vehicleId
label
latitude
longitude
bearing
speed
routeId
routeName
}
}Get all routes:
query {
routes {
routeId
routeShortName
routeLongName
}
}Get all stops:
query {
stops {
stopId
stopName
stopLat
stopLon
}
}Subscribe to all trip updates:
subscription {
feedUpdates {
timestamp
trips {
tripId
routeName
stopTimeUpdates {
stopName
arrival { delay }
}
}
}
}Subscribe to a specific route:
subscription {
feedUpdates(routeId: "7") {
timestamp
trips {
tripId
vehicle { label }
}
}
}GRT Static GTFS (zip) ──▶ H2 Database (routes, stops, trips)
│
GRT Realtime Feeds ──▶ Polling Service ──▶ In-Memory Cache ──▶ GraphQL API
(trip updates) (30s) │ │
(vehicle positions) Enrichment via H2 Queries + Subscriptions
Run with hot reloading:
./gradlew bootRun --continuousRun tests:
./gradlew test- Language: Kotlin
- Framework: Spring Boot 3.2
- API: GraphQL (Spring GraphQL + WebSocket subscriptions)
- Database: H2 (embedded, in-memory) for static GTFS data
- Data Source: GRT GTFS Realtime (protobuf) + Static GTFS