rewrite docs, part 1 📝
This commit is contained in:
parent
a47e20c1d4
commit
a794851e76
6 changed files with 352 additions and 33 deletions
319
docs/getting-started.md
Normal file
319
docs/getting-started.md
Normal file
|
@ -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!
|
9
docs/readme.md
Normal file
9
docs/readme.md
Normal file
|
@ -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)
|
41
docs/why.md
41
docs/why.md
|
@ -4,22 +4,24 @@
|
||||||
|
|
||||||
## Realtime Data
|
## 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
|
## 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.**
|
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
|
```js
|
||||||
{
|
{
|
||||||
cid: 'C-0',
|
cid: 'C-0',
|
||||||
date: '20171216',
|
date: '20171216',
|
||||||
dur: '043200',
|
|
||||||
chg: 2,
|
|
||||||
sDays: { /* … */ },
|
sDays: { /* … */ },
|
||||||
dep: {
|
dep: {
|
||||||
locX: 0,
|
locX: 0,
|
||||||
|
@ -67,7 +69,7 @@ to this one:
|
||||||
subway: true,
|
subway: true,
|
||||||
tram: true,
|
tram: true,
|
||||||
taxi: false
|
taxi: false
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
destination: {
|
destination: {
|
||||||
type: 'station',
|
type: 'station',
|
||||||
|
@ -78,40 +80,25 @@ to this one:
|
||||||
latitude: 48.150036,
|
latitude: 48.150036,
|
||||||
longitude: 11.461624
|
longitude: 11.461624
|
||||||
},
|
},
|
||||||
products: {
|
products: { /* … */ },
|
||||||
nationalExp: true,
|
|
||||||
national: true,
|
|
||||||
regionalExp: false,
|
|
||||||
regional: true,
|
|
||||||
suburban: true,
|
|
||||||
bus: true,
|
|
||||||
ferry: false,
|
|
||||||
subway: false,
|
|
||||||
tram: true,
|
|
||||||
taxi: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
departure: '2017-12-16T11:54:00.000+01:00',
|
departure: '2017-12-16T11:54:00.000+01:00',
|
||||||
arrival: '2017-12-16T16:26: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
|
||||||
|
|
||||||
[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/).
|
[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
|
## 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.
|
||||||
|
|
7
index.js
7
index.js
|
@ -1,7 +1,5 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const {readFileSync} = require('fs')
|
|
||||||
const {join} = require('path')
|
|
||||||
const createHafas = require('db-hafas')
|
const createHafas = require('db-hafas')
|
||||||
const createApi = require('hafas-rest-api')
|
const createApi = require('hafas-rest-api')
|
||||||
const createHealthCheck = require('hafas-client-health-check')
|
const createHealthCheck = require('hafas-client-health-check')
|
||||||
|
@ -10,8 +8,6 @@ const pkg = require('./package.json')
|
||||||
const stations = require('./routes/stations')
|
const stations = require('./routes/stations')
|
||||||
const station = require('./routes/station')
|
const station = require('./routes/station')
|
||||||
|
|
||||||
const docsAsMarkdown = readFileSync(join(__dirname, 'docs', 'index.md'), {encoding: 'utf8'})
|
|
||||||
|
|
||||||
const hafas = createHafas(pkg.name)
|
const hafas = createHafas(pkg.name)
|
||||||
|
|
||||||
const berlinHbf = '8011160'
|
const berlinHbf = '8011160'
|
||||||
|
@ -30,8 +26,7 @@ const config = {
|
||||||
description: pkg.description,
|
description: pkg.description,
|
||||||
homepage: pkg.homepage,
|
homepage: pkg.homepage,
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
docsAsMarkdown,
|
docsLink: 'https://github.com/derhuerst/db-rest/blob/5/docs/readme.md',
|
||||||
docsLink: '/docs',
|
|
||||||
logging: true,
|
logging: true,
|
||||||
aboutPage: true,
|
aboutPage: true,
|
||||||
etags: 'strong',
|
etags: 'strong',
|
||||||
|
|
|
@ -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).
|
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
|
## 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).
|
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).
|
||||||
|
|
Loading…
Reference in a new issue