2.5 KiB
MultiTrain
Search train connections from multiple start stations to multiple destination stations in one query. Results are merged, deduplicated, and ranked; each station can carry a penalty in minutes (e.g. "Wien Meidling +10 because it takes me longer to get there") that is added to a journey's duration for ranking only — displayed times stay real.
Data comes from Transitous, a community-run MOTIS instance aggregating open timetable data from many carriers (ÖBB, DB, and many more). Please respect their fair-use policy: set a USER_AGENT that identifies you.
Development
nix develop # provides node 22 + npm + typescript-language-server
npm install
npm run dev # dev server on http://127.0.0.1:3000
Note: tsx watch does not detect file changes on Windows-mounted drives (/mnt/* in WSL) — restart manually there.
Architecture
Lightweight hexagonal: src/model is the pure domain core (domain model, JourneySource port, search use case — fan-out over station pairs, merge, dedupe, penalty scoring). src/adapter holds the Fastify HTTP adapter and the Transitous adapter (the only place touching the MOTIS client libraries). src/main.ts wires everything by hand.
API
-
GET /api/locations?query=wien— station suggestions -
POST /api/search— body:{ "from": [{ "query": "Wien Hbf", "penalty": 0 }, { "query": "Wien Meidling", "penalty": 10 }], "to": [{ "query": "München Hbf", "penalty": 0 }], "departure": "2026-07-11T08:00:00Z" }departureandarrivalare mutually exclusive; omit both to depart now.
Configuration (environment)
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
HTTP port |
HOST |
127.0.0.1 |
Bind address |
USER_AGENT |
multitrain |
Sent to the MOTIS API — please personalize |
MOTIS_BASE_URL |
api.transitous.org | Self-hosted MOTIS instance |
Deployment on NixOS
# flake inputs
inputs.multitrain.url = "github:you/multitrain"; # or a path/git url
# NixOS configuration
{
imports = [ inputs.multitrain.nixosModules.default ];
services.multitrain = {
enable = true;
port = 3000;
host = "127.0.0.1";
userAgent = "multitrain (you@example.org)";
# motisBaseUrl = "https://motis.example.org";
};
}
nix build produces the server as result/bin/multitrain. After changing package-lock.json, refresh npmDepsHash in flake.nix (nix run nixpkgs#prefetch-npm-deps -- package-lock.json).