diff --git a/docs/index.md b/docs/api.md similarity index 100% rename from docs/index.md rename to docs/api.md diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..927bf4b --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,319 @@ +# Getting Started with `v5.db.transport.rest` + +Let's walk through the **requests that are necessary to implement a typical basic transit app**. + +*Note:* To properly & securely handle user input containing URL-unsafe characters, always [URL-encode](https://en.wikipedia.org/wiki/Percent-encoding) your query parameters! + +The following code snippets use [`curl`](https://curl.haxx.se) (a versatile command line HTTP tool) and [`jq`](https://stedolan.github.io/jq/) (the command line swiss army knife for processing JSON). + +### 1. search for stops + +The `/locations?query=…` route allows you to query stops, points of interest (POIs) & addresses. We're only interested in stops though, so we filter using `poi=false&addresses=false`: + +```shell +curl 'https://v5.db.transport.rest/locations?poi=false&addresses=false&query=südkreuz' -s | jq +``` + +```js +[ + { + "type": "stop", + "id": "8011113", + "name": "Berlin Südkreuz", + "location": { + "type": "location", + "id": "8011113", + "latitude": 52.47623, + "longitude": 13.365863 + }, + "products": { + "nationalExpress": true, + "national": true, + // … + } + }, + { + "type": "stop", + "id": "731654", + "name": "Südkreuz Bahnhof (S), Berlin", + "location": { + "type": "location", + "id": "731654", + "latitude": 52.476265, + "longitude": 13.3642 + }, + "products": { + "nationalExpress": true, + "national": true, + "regionalExp": true, + "regional": true, + "suburban": true, + "bus": true, + "ferry": false, + "subway": false, + "tram": false, + "taxi": false + } + }, + { + "type": "stop", + "id": "727256", + "name": "Südkreuz Bahnhof (S)/Ostseite, Berlin", + "location": { + "type": "location", + "id": "727256", + "latitude": 52.47436, + "longitude": 13.366843 + }, + "products": { + // … + } + }, + // … +] +``` + +### 2. fetch departures at a stop + +Let's fetch 5 of the next departures at *Berlin Südkreuz* (which has the ID `8011113`): + +```shell +curl 'https://v5.db.transport.rest/stops/8011113/departures?results=5' -s | jq +``` + +```js +[ + { + "tripId": "1|1168945|24|80|1052020", + "direction": "Schöneberg, Reichartstr.", + "line": { + "type": "line", + "id": "5-vbbbvb-248", + "name": "Bus 248", + "mode": "bus", + "product": "bus", + // … + }, + + "when": "2020-05-01T18:39:00+02:00", + "plannedWhen": "2020-05-01T18:38:00+02:00", + "delay": 60, + "platform": null, + "plannedPlatform": null, + + "stop": { + "type": "stop", + "id": "727256", + "name": "Südkreuz Bahnhof (S)/Ostseite, Berlin", + "location": { /* … */ }, + "products": { /* … */ }, + "station": { + "type": "station", + "id": "8011113", + "name": "Berlin Südkreuz", + "location": { /* … */ }, + "products": { /* … */ }, + } + }, + + "remarks": [], + }, + // … + { + "tripId": "1|322308|0|80|1052020", + "direction": "Lutherstadt Wittenberg Hbf", + "line": { + "type": "line", + "id": "re-3", + "name": "RE 3", + "mode": "train", + "product": "regional", + // … + }, + + "when": "2020-05-01T18:40:00+02:00", + "plannedWhen": "2020-05-01T18:41:00+02:00", + "delay": -60, + "platform": "6", + "plannedPlatform": "6", + + "stop": { + "type": "stop", + "id": "8011113", + "name": "Berlin Südkreuz", + "location": { /* … */ }, + "products": { /* … */ }, + }, + + "remarks": [ /* … */ ], + }, + // … +] +``` + +Note that `when` includes the `delay`, and `plannedWhen` does not. + +### 3. fetch journeys from A to B + +We call a connection from A to B – at a specific date & time, made up of sections on specific *trips* – `journey`. + +Let's fetch 2 journeys from `8011113` (*Berlin Südkreuz*) to `8010159` (*Halle (Saale)Hbf*), departing tomorrow at 2pm (at the time of writing this). + +```shell +curl 'https://v5.db.transport.rest/journeys?from=8011113&to=8010159&departure=tomorrow+2pm&results=2' -s | jq +``` + +```js +{ + "journeys": [{ + // 1st journey + "type": "journey", + "legs": [{ + // 1st leg + "tripId": "1|310315|0|80|2052020", + "direction": "München Hbf", + "line": { + "type": "line", + "id": "ice-1601", + "name": "ICE 1601", + "mode": "train", + "product": "nationalExpress", + // … + }, + + "origin": { + "type": "stop", + "id": "8011113", + "name": "Berlin Südkreuz", + "location": { /* … */ }, + "products": { /* … */ }, + }, + "departure": "2020-05-02T14:37:00+02:00", + "plannedDeparture": "2020-05-02T14:37:00+02:00", + "departureDelay": null, + "departurePlatform": "3", + "plannedDeparturePlatform": "3" + + "destination": { + "type": "stop", + "id": "8010205", + "name": "Leipzig Hbf", + "location": { /* … */ }, + "products": { /* … */ }, + }, + "arrival": "2020-05-02T15:42:00+02:00", + "plannedArrival": "2020-05-02T15:42:00+02:00", + "arrivalDelay": null, + "arrivalPlatform": "11", + "plannedArrivalPlatform": "11", + // … + }, { + // 2nd leg + "walking": true, + "distance": 116, + + "origin": { + "type": "stop", + "id": "8010205", + "name": "Leipzig Hbf", + "location": { /* … */ }, + "products": { /* … */ }, + }, + "departure": "2020-05-02T15:42:00+02:00", + "plannedDeparture": "2020-05-02T15:42:00+02:00", + "departureDelay": null, + + "destination": { + "type": "stop", + "id": "8098205", + "name": "Leipzig Hbf (tief)", + "location": { /* … */ }, + "products": { /* … */ },, + "station": { + "type": "station", + "id": "8010205", + "name": "Leipzig Hbf", + "location": { /* … */ }, + "products": { /* … */ }, + } + }, + "arrival": "2020-05-02T15:51:00+02:00", + "plannedArrival": "2020-05-02T15:51:00+02:00", + "arrivalDelay": null, + // … + }, { + // 3rd leg + "tripId": "1|334376|4|80|2052020", + "direction": "Halle(Saale)Hbf", + "line": { + "type": "line", + "id": "4-800486-5", + "name": "S 5", + "mode": "train", + "product": "suburban", + // … + }, + + "origin": { + "type": "stop", + "id": "8098205", + "name": "Leipzig Hbf (tief)", + "location": { /* … */ }, + "products": { /* … */ },, + "station": { + "type": "station", + "id": "8010205", + "name": "Leipzig Hbf", + "location": { /* … */ }, + "products": { /* … */ }, + } + }, + "departure": "2020-05-02T15:53:00+02:00", + "plannedDeparture": "2020-05-02T15:53:00+02:00", + "departureDelay": null, + "departurePlatform": "2", + "plannedDeparturePlatform": "2", + + "destination": { + "type": "stop", + "id": "8010159", + "name": "Halle(Saale)Hbf", + "location": { /* … */ }, + "products": { /* … */ }, + }, + "arrival": "2020-05-02T16:19:00+02:00", + "plannedArrival": "2020-05-02T16:19:00+02:00", + "arrivalDelay": null, + "arrivalPlatform": "13", + "plannedArrivalPlatform": "13", + + "cycle": {"min": 600, "max": 1800, "nr": 7}, + "alternatives": [ + { + "tripId": "1|333647|0|80|2052020", + "direction": "Halle(Saale)Hbf", + "line": { /* … */ }, + "when": "2020-05-02T16:03:00+02:00", + "plannedWhen": "2020-05-02T16:03:00+02:00", + "delay": null, + }, + // … + ], + // … + }], + }, { + // 2nd journey + "type": "journey", + "legs": [ /* … */ ], + // … + }], + + // … +} +``` + +Note that `departure` includes the `departureDelay`, and `arrival` includes the `arrivalDelay`. `plannedDeparture` and `plannedArrival` do not. + +### 4. more features + +These are the basics. Check the full [API docs](api.md) for all features! diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..8e32211 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,9 @@ +# `v5.db.transport.rest` documentation + +[`v5.db.transport.rest`](https://v5.db.transport.rest/) is a [REST API](https://restfulapi.net). + +You can just use the API without authentication. In the future, we may [rate-limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) your IP address if you make too many requests. + +- [Getting Started](getting-started.md) +- [API documentation](api.md) +- [Why this API?](why.md) diff --git a/docs/why.md b/docs/why.md index 58da037..c671bcd 100644 --- a/docs/why.md +++ b/docs/why.md @@ -4,22 +4,24 @@ ## Realtime Data -This API returns realtime data whenever its upstream, the [API for DB's mobile app](https://gist.github.com/derhuerst/2a735268bd82a0a6779633f15dceba33), provides it. +This API returns realtime data whenever its upstream, the [API for DB's mobile app](https://github.com/public-transport/hafas-client/blob/e02a20b1de59bda3cd380445b6105e4c46036636/p/db/readme.md), provides it. ## No API Key Especially on web sites/apps, it is a subpar solution to the send API keys to the client. Also, you have to obtain these keys manually and cannot automatically revoke them. **This API doesn't require a key.** -## Sane Markup +## CORS -Compare the official API: +If you want to use transport information on a web site/app, [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) must be enabled. Otherwise, you would have to send all requests through your own proxy server. **This API has CORS enabled.** + +## Readable Markup + +Compare the underlying HAFAS API: ```js { cid: 'C-0', date: '20171216', - dur: '043200', - chg: 2, sDays: { /* … */ }, dep: { locX: 0, @@ -67,7 +69,7 @@ to this one: subway: true, tram: true, taxi: false - } + }, }, destination: { type: 'station', @@ -78,40 +80,25 @@ to this one: latitude: 48.150036, longitude: 11.461624 }, - products: { - nationalExp: true, - national: true, - regionalExp: false, - regional: true, - suburban: true, - bus: true, - ferry: false, - subway: false, - tram: true, - taxi: false - } + products: { /* … */ }, }, departure: '2017-12-16T11:54:00.000+01:00', arrival: '2017-12-16T16:26:00.000+01:00', - price: { - amount: 150, - hint: null - } } ``` -## GZIP support +## Caching-friendly -Especially on cellular connections, gzipped responses improve the performance a lot. +This API sends [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) & [`Cache-Control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) headers, allowing clients to refresh their state efficiently. ## HTTP/2 [HTTP/2](https://http2.github.io/) allows multiple requests at a time, efficiently pipelines sequential requests and compresses headers. See [Cloudflare's HTTP/2 page](https://blog.cloudflare.com/http-2-for-web-developers/). -## More Features +## Proper HTTP, Proper REST -This API enhances the functionality of their API with static data, which is used in e.g. `GET /stations`. +This wrapper API follows [REST-ful design principles](https://restfulapi.net), it uses `GET`, and proper paths & headers. ## Monitoring -There's a [status board](https://status.transport.rest) that indicates the health of the API endpoints. +There's a [status board](https://status.transport.rest) that shows realtime uptime statistics. diff --git a/index.js b/index.js index 50eaa86..23bd293 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,5 @@ 'use strict' -const {readFileSync} = require('fs') -const {join} = require('path') const createHafas = require('db-hafas') const createApi = require('hafas-rest-api') const createHealthCheck = require('hafas-client-health-check') @@ -10,8 +8,6 @@ const pkg = require('./package.json') const stations = require('./routes/stations') const station = require('./routes/station') -const docsAsMarkdown = readFileSync(join(__dirname, 'docs', 'index.md'), {encoding: 'utf8'}) - const hafas = createHafas(pkg.name) const berlinHbf = '8011160' @@ -30,8 +26,7 @@ const config = { description: pkg.description, homepage: pkg.homepage, version: pkg.version, - docsAsMarkdown, - docsLink: '/docs', + docsLink: 'https://github.com/derhuerst/db-rest/blob/5/docs/readme.md', logging: true, aboutPage: true, etags: 'strong', diff --git a/readme.md b/readme.md index 0bbb8b5..e8d34f5 100644 --- a/readme.md +++ b/readme.md @@ -36,6 +36,15 @@ npm start To keep the API running permanently, use tools like [`forever`](https://github.com/foreverjs/forever#forever) or [`systemd`](https://wiki.debian.org/systemd). +## Related Projects + +- [`vbb-rest`](https://github.com/derhuerst/vbb-rest) – A clean REST API wrapping around the VBB API. +- [`bvg-rest`](https://github.com/derhuerst/bvg-rest) – A clean REST API wrapping around the BVG API. +- [`hvv-rest`](https://github.com/derhuerst/hvv-rest) – A clean REST API wrapping around the HVV API. +- [`hafas-rest-api`](https://github.com/public-transport/hafas-rest-api) – Expose a HAFAS client via an HTTP REST API. +- [`hafas-client`](https://github.com/public-transport/hafas-client) – JavaScript client for HAFAS public transport APIs. + + ## Contributing If you **have a question**, **found a bug** or want to **propose a feature**, have a look at [the issues page](https://github.com/derhuerst/db-rest/issues).