A simple train departure tracker built for the DPS Technical Challenge. It lets users search for a station by partial name and see all upcoming departures within the next 15 minutes.
- Python 3.14.6
- FastAPI
- httpx
- React
- Vite
- Search stations by partial name
- Minimum 3-character validation
- Retrieves departures from iRail API
- Shows departures within the next 15 minutes
- Displays:
- Station name
- Train Number (e.g.
IC1818) - Destination
- Scheduled Departure Time
- Delay Minutes
- Cancelled Status
train-tracker/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app, route definitions
│ │ ├── irail.py # iRail API client (stations + departures)
│ │ └── services.py # Business logic: filtering, formatting
│ └── requirements.txt
│
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Root component, search state
│ │ ├── api.js # Fetch wrapper for /departures
│ │ └── components/
│ │ └── DeparturesTable.jsx # Results table grouped by station
│ └── package.json
│
├── README.md
└── AI_USAGE.md
cd backend
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
fastapi dev app/main.pyBackend runs on:
cd frontend
npm install
npm run devFrontend runs on:
GET /departures?q=Bru| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Yes | Station name substring to search for |
Queries shorter than 3 characters return an error response.
Example:
GET /departures?q=BrResponse:
{
"detail": "Query must be at least 3 characters long"
}{
"query": "Bru",
"stations_found": 16,
"departures_count": 10,
"departures": [
{
"station": "Brugge",
"trainNumber": "BE.NMBS.IC1818",
"destination": "Antwerp-Central",
"scheduledTime": "2025-06-21T16:25:00+00:00",
"delayMinutes": 0,
"cancelled": false
}
]
}I chose FastAPI because it is lightweight, easy to develop with, and provides automatic API documentation through Swagger UI.
React was selected because it is widely used, simple to integrate with REST APIs, and aligns with the preferred technology stack mentioned in the challenge.
The station list is loaded once from the iRail API and cached in memory. This reduces unnecessary network requests and improves response times.
The iRail API may return many departures. To satisfy the challenge requirements, departures are filtered to only include trains departing within the next 15 minutes.
The API validates user input and returns a clear error message when the search query is shorter than three characters.
- Station matching is case-insensitive substring search (e.g.
"bru"matches"Brugge"and"Brussel-Zuid"). - Time calculations are performed using UTC timestamps returned by the iRail API.
- Only upcoming departures within the next 15 minutes are included in results.
- No fuzzy matching is implemented.
- Station data is stored in memory and is refreshed only when the backend restarts.
- No pagination is implemented for large result sets.
- UI is intentionally simple and focused on functionality.
- Add fuzzy search support (e.g. "Antverpen" → "Antwerpen-Centraal").
- Add loading skeletons and improved user feedback.
- Group departures by station in the UI.
- Add automated tests.
- Deploy backend and frontend to cloud platforms.
This project uses the public iRail API:
Created as part of the Digital Product School Technical Challenge.