Compare commits
51 commits
Author | SHA1 | Date | |
---|---|---|---|
|
f0901ed56c | ||
|
cf63dff36b | ||
|
be8aee3fbf | ||
|
58c18827ca | ||
|
5fffdb88d2 | ||
|
4517d41e2b | ||
|
0d92cb7ed0 | ||
|
3e5dc8e733 | ||
|
4be921d5dd | ||
|
399713695e | ||
|
66ca7d32e5 | ||
|
76358235ab | ||
|
6a94c19dc8 | ||
|
291006c81e | ||
|
3941e5f4ea | ||
|
e8b040e601 | ||
|
d53ff22d53 | ||
|
a1050c9d90 | ||
|
40ae288388 | ||
|
9793b471f2 | ||
|
8de11ee2cd | ||
|
a294db5cd4 | ||
|
1eff75e491 | ||
|
f0c0cc698b | ||
|
c087d49f16 | ||
|
8482862f76 | ||
|
3fe3d4ac7b | ||
|
5b4a97a93c | ||
|
d1be6cb7af | ||
|
2339f1752a | ||
|
11aa62d829 | ||
|
c34f218373 | ||
|
e038ce37de | ||
|
30a56869ce | ||
|
303544277b | ||
|
a5688cabe6 | ||
|
63964f91f3 | ||
|
9879365e9b | ||
|
ba256397d0 | ||
|
1047946a23 | ||
|
5658c84c54 | ||
|
448294ba5c | ||
|
779e67886b | ||
|
cea4439e11 | ||
|
44b7d6f3ce | ||
|
2ef8bbe6df | ||
|
04543f0146 | ||
|
82a8a97e0f | ||
|
6f2e5f134b | ||
|
fe469cd658 | ||
|
4b333ea9be |
24 changed files with 6591 additions and 823 deletions
|
@ -1,12 +1,11 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"es2022": true,
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
"sourceType": "module"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"node_modules"
|
||||
|
|
13
.github/renovate.json
vendored
Normal file
13
.github/renovate.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"configMigration": true,
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true,
|
||||
"automerge": true,
|
||||
"automergeType": "branch",
|
||||
"ignoreTests": true
|
||||
}
|
||||
}
|
46
.github/workflows/docker-image.yml
vendored
46
.github/workflows/docker-image.yml
vendored
|
@ -2,21 +2,11 @@ name: build & publish Docker image
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5
|
||||
- 6
|
||||
jobs:
|
||||
lint-test:
|
||||
name: lint & test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: setup Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
- run: npm install
|
||||
- run: npm run lint
|
||||
- run: npm test
|
||||
name: lint, build & test
|
||||
uses: './.github/workflows/lint-test.yml'
|
||||
|
||||
build-and-publish:
|
||||
name: build & publish Docker image
|
||||
|
@ -24,23 +14,32 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: check out the repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: configure Docker to use buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
- name: build Docker image & push to Docker Hub
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
derhuerst/db-rest:5
|
||||
derhuerst/db-rest:6
|
||||
derhuerst/db-rest:latest
|
||||
# https://docs.docker.com/build/ci/github-actions/examples/#github-cache
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max,oci-mediatypes=true,compression=zstd
|
||||
|
||||
# this is for the public-transport/infrastructure cluster
|
||||
- name: log in to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
@ -52,7 +51,14 @@ jobs:
|
|||
id: datetime
|
||||
run: echo "::set-output name=datetime::$(date -u +'%Y-%m-%dT%H.%M.%SZ')"
|
||||
- name: push Docker image to GitHub Registry
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ghcr.io/${{github.repository}}:v5_${{steps.hash.outputs.hash}}_${{steps.datetime.outputs.datetime}}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
ghcr.io/${{github.repository}}:v6
|
||||
ghcr.io/${{github.repository}}:v6_${{steps.hash.outputs.hash}}_${{steps.datetime.outputs.datetime}}
|
||||
# https://docs.docker.com/build/ci/github-actions/examples/#github-cache
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max,oci-mediatypes=true,compression=zstd
|
||||
|
|
35
.github/workflows/lint-test.yml
vendored
Normal file
35
.github/workflows/lint-test.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
name: lint, build & test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
lint-test:
|
||||
name: lint, build & test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- '16.x'
|
||||
- '18.x'
|
||||
- '20.x'
|
||||
- '22.x'
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- run: npm run lint
|
||||
- run: npm run build
|
||||
- run: npm test
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,8 +5,8 @@ Thumbs.db
|
|||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
/package-lock.json
|
||||
/dump.rdb
|
||||
|
||||
/docs/*.html
|
||||
/docs/syntax.css
|
||||
/docs/api.md
|
||||
|
|
14
Dockerfile
14
Dockerfile
|
@ -1,10 +1,10 @@
|
|||
FROM node:alpine as builder
|
||||
FROM node:18-alpine as builder
|
||||
WORKDIR /app
|
||||
|
||||
# install dependencies
|
||||
RUN apk add --update git bash
|
||||
ADD package.json /app
|
||||
RUN npm install
|
||||
ADD package.json package-lock.json /app
|
||||
RUN npm ci
|
||||
|
||||
# build documentation
|
||||
ADD . /app
|
||||
|
@ -12,13 +12,13 @@ RUN npm run build
|
|||
|
||||
# ---
|
||||
|
||||
FROM node:alpine
|
||||
FROM node:18-alpine
|
||||
LABEL org.opencontainers.image.title="db-rest"
|
||||
LABEL org.opencontainers.image.description="A clean REST API wrapping around the Deutsche Bahn API."
|
||||
LABEL org.opencontainers.image.authors="Jannis R <mail@jannisr.de>"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/derhuerst/db-rest/tree/5"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/derhuerst/db-rest/tree/6"
|
||||
LABEL org.opencontainers.image.source="https://github.com/derhuerst/db-rest"
|
||||
LABEL org.opencontainers.image.revision="5"
|
||||
LABEL org.opencontainers.image.revision="6"
|
||||
LABEL org.opencontainers.image.licenses="ISC"
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -32,7 +32,7 @@ COPY --from=builder /app/docs ./docs
|
|||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV HOSTNAME v5.db.transport.rest
|
||||
ENV HOSTNAME v6.db.transport.rest
|
||||
ENV PORT 3000
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
|
|
47
api.js
47
api.js
|
@ -1,29 +1,36 @@
|
|||
'use strict'
|
||||
// todo: use import assertions once they're supported by Node.js & ESLint
|
||||
// https://github.com/tc39/proposal-import-assertions
|
||||
import {createRequire} from 'node:module'
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
import {dirname, join as pathJoin} from 'node:path'
|
||||
import {fileURLToPath} from 'node:url'
|
||||
import {createDbHafas as createHafas} from 'db-hafas'
|
||||
import {createHafasRestApi} from 'hafas-rest-api'
|
||||
import createHealthCheck from 'hafas-client-health-check'
|
||||
import Redis from 'ioredis'
|
||||
import {createCachedHafasClient} from 'cached-hafas-client'
|
||||
import {createRedisStore} from 'cached-hafas-client/stores/redis.js'
|
||||
import serveStatic from 'serve-static'
|
||||
import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js'
|
||||
import {loyaltyCardParser} from './lib/loyalty-cards.js'
|
||||
import {route as stations} from './routes/stations.js'
|
||||
import {route as station} from './routes/station.js'
|
||||
|
||||
const createHafas = require('db-hafas')
|
||||
const createApi = require('hafas-rest-api')
|
||||
const createHealthCheck = require('hafas-client-health-check')
|
||||
const Redis = require('ioredis')
|
||||
const withCache = require('cached-hafas-client')
|
||||
const redisStore = require('cached-hafas-client/stores/redis')
|
||||
const {join: pathJoin} = require('path')
|
||||
const serveStatic = require('serve-static')
|
||||
const {parseBoolean} = require('hafas-rest-api/lib/parse')
|
||||
const pkg = require('./package.json')
|
||||
const {loyaltyCardParser} = require('./lib/loyalty-cards')
|
||||
const stations = require('./routes/stations')
|
||||
const station = require('./routes/station')
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const docsRoot = pathJoin(__dirname, 'docs')
|
||||
|
||||
const berlinHbf = '8011160'
|
||||
|
||||
// todo: use process.env.HAFAS_USER_AGENT if defined
|
||||
let hafas = createHafas(pkg.name)
|
||||
let healthCheck = createHealthCheck(hafas, berlinHbf)
|
||||
|
||||
if (process.env.REDIS_URL) {
|
||||
const redis = new Redis(process.env.REDIS_URL || null)
|
||||
hafas = withCache(hafas, redisStore(redis), {
|
||||
hafas = createCachedHafasClient(hafas, createRedisStore(redis), {
|
||||
cachePeriods: {
|
||||
locations: 6 * 60 * 60 * 1000, // 6h
|
||||
},
|
||||
|
@ -54,6 +61,12 @@ const mapRouteParsers = (route, parsers) => {
|
|||
default: 'false',
|
||||
parse: parseBoolean,
|
||||
},
|
||||
age: {
|
||||
description: 'Age of traveller',
|
||||
type: 'integer',
|
||||
defaultStr: '*adult*',
|
||||
parse: parseInteger
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +83,7 @@ const config = {
|
|||
description: pkg.description,
|
||||
homepage: pkg.homepage,
|
||||
version: pkg.version,
|
||||
docsLink: 'https://github.com/derhuerst/db-rest/blob/5/docs/readme.md',
|
||||
docsLink: 'https://github.com/derhuerst/db-rest/blob/6/docs/readme.md',
|
||||
openapiSpec: true,
|
||||
logging: true,
|
||||
aboutPage: false,
|
||||
|
@ -81,13 +94,13 @@ const config = {
|
|||
modifyRoutes,
|
||||
}
|
||||
|
||||
const api = createApi(hafas, config, (api) => {
|
||||
const api = await createHafasRestApi(hafas, config, (api) => {
|
||||
api.use('/', serveStatic(docsRoot, {
|
||||
extensions: ['html', 'htm'],
|
||||
}))
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
hafas,
|
||||
config,
|
||||
api,
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<rect class="box" x="0" y="0" width="100" height="41" />
|
||||
<text class="monospace-text" x="26.2" y="13">db-rest</text>
|
||||
<text class="normal-text small-text" x="4" y="26">deployed at</text>
|
||||
<text class="monospace-text small-text" x="4" y="36">v5.db.transport.rest</text>
|
||||
<text class="monospace-text small-text" x="4" y="36">v6.db.transport.rest</text>
|
||||
</g>
|
||||
<g transform="translate(269, 14)">
|
||||
<path class="arrow" d="M0,14 L50,14" />
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -1,16 +1,14 @@
|
|||
'use strict'
|
||||
|
||||
const generateApiDocs = require('hafas-rest-api/tools/generate-docs')
|
||||
const {api} = require('./api')
|
||||
import {generateApiDocs} from 'hafas-rest-api/tools/generate-docs.js'
|
||||
import {api} from '../api.js'
|
||||
|
||||
const HEAD = `\
|
||||
# \`v5.db.transport.rest\` API documentation
|
||||
# \`v6.db.transport.rest\` API documentation
|
||||
|
||||
[\`v5.db.transport.rest\`](https://v5.db.transport.rest/) is a [REST API](https://restfulapi.net). Data is being returned as [JSON](https://www.json.org/).
|
||||
[\`v6.db.transport.rest\`](https://v6.db.transport.rest/) is a [REST API](https://restfulapi.net). Data is being returned as [JSON](https://www.json.org/).
|
||||
|
||||
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 request/minute (burst 200 requests/minute) set up.
|
||||
|
||||
[OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv5.db.transport.rest%2F.well-known%2Fservice-desc%0A)
|
||||
[OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv6.db.transport.rest%2F.well-known%2Fservice-desc%0A)
|
||||
|
||||
*Note:* The examples snippets in this documentation uses the \`url-encode\` CLI tool of the [\`url-decode-encode-cli\` package](https://www.npmjs.com/package/url-decode-encode-cli) for [URL-encoding](https://de.wikipedia.org/wiki/URL-Encoding).
|
||||
`
|
||||
|
@ -32,22 +30,22 @@ const order = [
|
|||
|
||||
const descriptions = {
|
||||
'/locations': `\
|
||||
Uses [\`hafasClient.locations()\`](https://github.com/public-transport/hafas-client/blob/5/docs/locations.md) to **find stops/stations, POIs and addresses matching \`query\`**.
|
||||
Uses [\`hafasClient.locations()\`](https://github.com/public-transport/hafas-client/blob/6/docs/locations.md) to **find stops/stations, POIs and addresses matching \`query\`**.
|
||||
`,
|
||||
'/stops/nearby': `\
|
||||
Uses [\`hafasClient.nearby()\`](https://github.com/public-transport/hafas-client/blob/5/docs/nearby.md) to **find stops/stations close to the given geolocation**.
|
||||
Uses [\`hafasClient.nearby()\`](https://github.com/public-transport/hafas-client/blob/6/docs/nearby.md) to **find stops/stations close to the given geolocation**.
|
||||
`,
|
||||
'/stops/reachable-from': `\
|
||||
Uses [\`hafasClient.reachableFrom()\`](https://github.com/public-transport/hafas-client/blob/5/docs/reachable-from.md) to **find stops/stations reachable within a certain time from an address**.
|
||||
Uses [\`hafasClient.reachableFrom()\`](https://github.com/public-transport/hafas-client/blob/6/docs/reachable-from.md) to **find stops/stations reachable within a certain time from an address**.
|
||||
`,
|
||||
'/stops/:id': `\
|
||||
Uses [\`hafasClient.stop()\`](https://github.com/public-transport/hafas-client/blob/5/docs/stop.md) to **find a stop/station by ID**.
|
||||
Uses [\`hafasClient.stop()\`](https://github.com/public-transport/hafas-client/blob/6/docs/stop.md) to **find a stop/station by ID**.
|
||||
`,
|
||||
'/stops/:id/departures': `\
|
||||
Uses [\`hafasClient.departures()\`](https://github.com/public-transport/hafas-client/blob/5/docs/departures.md) to **get departures at a stop/station**.
|
||||
Uses [\`hafasClient.departures()\`](https://github.com/public-transport/hafas-client/blob/6/docs/departures.md) to **get departures at a stop/station**.
|
||||
`,
|
||||
'/stops/:id/arrivals': `\
|
||||
Works like [\`/stops/:id/departures\`](#get-stopsiddepartures), except that it uses [\`hafasClient.arrivals()\`](https://github.com/public-transport/hafas-client/blob/5/docs/arrivals.md) to **arrivals at a stop/station**.
|
||||
Works like [\`/stops/:id/departures\`](#get-stopsiddepartures), except that it uses [\`hafasClient.arrivals()\`](https://github.com/public-transport/hafas-client/blob/6/docs/arrivals.md) to **arrivals at a stop/station**.
|
||||
`,
|
||||
'/stations': `\
|
||||
If the \`query\` parameter is used, it will use [\`db-stations-autocomplete@2\`](https://github.com/derhuerst/db-stations-autocomplete/tree/2.2.0) to autocomplete *Deutsche Bahn*-operated stops/stations. Otherwise, it will filter the stops/stations in [\`db-stations@3\`](https://github.com/derhuerst/db-stations/tree/3.0.1).
|
||||
|
@ -58,32 +56,32 @@ Instead of receiving a JSON response, you can request [newline-delimited JSON](h
|
|||
Returns a stop/station from [\`db-stations\`](https://npmjs.com/package/db-stations).
|
||||
`,
|
||||
'/journeys': `\
|
||||
Uses [\`hafasClient.journeys()\`](https://github.com/public-transport/hafas-client/blob/5/docs/journeys.md) to **find journeys from A (\`from\`) to B (\`to\`)**.
|
||||
Uses [\`hafasClient.journeys()\`](https://github.com/public-transport/hafas-client/blob/6/docs/journeys.md) to **find journeys from A (\`from\`) to B (\`to\`)**.
|
||||
|
||||
\`from\` (A), \`to\` (B), and the optional \`via\` must each have one of these formats:
|
||||
|
||||
- as stop/station ID (e.g. \`from=8010159\` for *Halle (Saale) Hbf*)
|
||||
- as a POI (e.g. \`from.id=991561765&from.latitude=51.48364&from.longitude=11.98084\` for *Halle+(Saale),+Stadtpark+Halle+(Grünanlagen)*)
|
||||
- as a POI (e.g. \`from.id=991561765&from.latitude=51.48364&from.longitude=11.98084&from.name=Halle+(Saale),+Stadtpark+Halle+(Grünanlagen)\` for *Halle (Saale), Stadtpark Halle (Grünanlagen)*)
|
||||
- as an address (e.g. \`from.latitude=51.25639&from.longitude=7.46685&from.address=Hansestadt+Breckerfeld,+Hansering+3\` for *Hansestadt Breckerfeld, Hansering 3*)
|
||||
|
||||
### Pagination
|
||||
|
||||
Given a response, you can also fetch more journeys matching the same criteria. Instead of \`from*\`, \`to*\` & \`departure\`/\`arrival\`, pass \`earlierRef\` from the first response as \`earlierThan\` to get journeys "before", or \`laterRef\` as \`laterThan\` to get journeys "after".
|
||||
|
||||
Check the [\`hafasClient.journeys()\` docs](https://github.com/public-transport/hafas-client/blob/5/docs/journeys.md) for more details.
|
||||
Check the [\`hafasClient.journeys()\` docs](https://github.com/public-transport/hafas-client/blob/6/docs/journeys.md) for more details.
|
||||
`,
|
||||
'/journeys/:ref': `\
|
||||
Uses [\`hafasClient.refreshJourney()\`](https://github.com/public-transport/hafas-client/blob/5/docs/refresh-journey.md) to **"refresh" a journey, using its \`refreshToken\`**.
|
||||
Uses [\`hafasClient.refreshJourney()\`](https://github.com/public-transport/hafas-client/blob/6/docs/refresh-journey.md) to **"refresh" a journey, using its \`refreshToken\`**.
|
||||
|
||||
The journey will be the same (equal \`from\`, \`to\`, \`via\`, date/time & vehicles used), but you can get up-to-date realtime data, like delays & cancellations.
|
||||
`,
|
||||
'/trips/:id': `\
|
||||
Uses [\`hafasClient.trip()\`](https://github.com/public-transport/hafas-client/blob/5/docs/trip.md) to **fetch a trip by ID**.
|
||||
Uses [\`hafasClient.trip()\`](https://github.com/public-transport/hafas-client/blob/6/docs/trip.md) to **fetch a trip by ID**.
|
||||
|
||||
A trip is a specific vehicle, stopping at a series of stops at specific points in time. Departures, arrivals & journey legs reference trips by their ID.
|
||||
`,
|
||||
'/radar': `\
|
||||
Uses [\`hafasClient.radar()\`](https://github.com/public-transport/hafas-client/blob/5/docs/radar.md) to **find all vehicles currently in an area**, as well as their movements.
|
||||
Uses [\`hafasClient.radar()\`](https://github.com/public-transport/hafas-client/blob/6/docs/radar.md) to **find all vehicles currently in an area**, as well as their movements.
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -92,7 +90,7 @@ const examples = {
|
|||
### Example
|
||||
|
||||
\`\`\`shell
|
||||
curl 'https://v5.db.transport.rest/locations?query=halle&results=1' -s | jq
|
||||
curl 'https://v6.db.transport.rest/locations?query=halle&results=1' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -120,7 +118,7 @@ curl 'https://v5.db.transport.rest/locations?query=halle&results=1' -s | jq
|
|||
### Example
|
||||
|
||||
\`\`\`shell
|
||||
curl 'https://v5.db.transport.rest/stops/nearby?latitude=53.5711&longitude=10.0015' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stops/nearby?latitude=53.5711&longitude=10.0015' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -160,7 +158,7 @@ curl 'https://v5.db.transport.rest/stops/nearby?latitude=53.5711&longitude=10.00
|
|||
### Example
|
||||
|
||||
\`\`\`shell
|
||||
curl 'https://v5.db.transport.rest/stops/reachable-from?latitude=53.553766&longitude=9.977514&address=Hamburg,+Holstenwall+9' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stops/reachable-from?latitude=53.553766&longitude=9.977514&address=Hamburg,+Holstenwall+9' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -200,7 +198,7 @@ curl 'https://v5.db.transport.rest/stops/reachable-from?latitude=53.553766&longi
|
|||
### Example
|
||||
|
||||
\`\`\`shell
|
||||
curl 'https://v5.db.transport.rest/stops/8010159' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stops/8010159' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -229,7 +227,7 @@ curl 'https://v5.db.transport.rest/stops/8010159' -s | jq
|
|||
|
||||
\`\`\`shell
|
||||
# autocomplete using db-stations-autocomplete
|
||||
curl 'https://v5.db.transport.rest/stations?query=dammt' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stations?query=dammt' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -265,7 +263,7 @@ curl 'https://v5.db.transport.rest/stations?query=dammt' -s | jq
|
|||
|
||||
\`\`\`shell
|
||||
# filter db-stations by \`hasParking\` property
|
||||
curl 'https://v5.db.transport.rest/stations?hasParking=true' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stations?hasParking=true' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -287,7 +285,7 @@ curl 'https://v5.db.transport.rest/stations?hasParking=true' -s | jq
|
|||
|
||||
\`\`\`shell
|
||||
# filter db-stations by \`hasDBLounge\` property, get newline-delimited JSON
|
||||
curl 'https://v5.db.transport.rest/stations?hasDBLounge=true' -H 'accept: application/x-ndjson' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stations?hasDBLounge=true' -H 'accept: application/x-ndjson' -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
'/stations/:id': `\
|
||||
|
@ -295,9 +293,9 @@ curl 'https://v5.db.transport.rest/stations?hasDBLounge=true' -H 'accept: applic
|
|||
|
||||
\`\`\`shell
|
||||
# lookup Halle (Saale) Hbf
|
||||
curl 'https://v5.db.transport.rest/stations/8010159' -s | jq
|
||||
curl 'https://v5.db.transport.rest/stations/LH' -s | jq # RIL100/DS100
|
||||
curl 'https://v5.db.transport.rest/stations/LHG' -s | jq # RIL100/DS100
|
||||
curl 'https://v6.db.transport.rest/stations/8010159' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stations/LH' -s | jq # RIL100/DS100
|
||||
curl 'https://v6.db.transport.rest/stations/LHG' -s | jq # RIL100/DS100
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -328,7 +326,7 @@ curl 'https://v5.db.transport.rest/stations/LHG' -s | jq # RIL100/DS100
|
|||
|
||||
\`\`\`shell
|
||||
# at Halle (Saale) Hbf, in direction Berlin Südkreuz
|
||||
curl 'https://v5.db.transport.rest/stops/8010159/departures?direction=8011113&duration=120' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stops/8010159/departures?direction=8011113&duration=120' -s | jq
|
||||
\`\`\`
|
||||
|
||||
\`\`\`js
|
||||
|
@ -370,7 +368,7 @@ curl 'https://v5.db.transport.rest/stops/8010159/departures?direction=8011113&du
|
|||
|
||||
\`\`\`shell
|
||||
# at Halle (Saale) Hbf, 10 minutes
|
||||
curl 'https://v5.db.transport.rest/stops/8010159/arrivals?duration=10' -s | jq
|
||||
curl 'https://v6.db.transport.rest/stops/8010159/arrivals?duration=10' -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
'/journeys': `\
|
||||
|
@ -378,9 +376,9 @@ curl 'https://v5.db.transport.rest/stops/8010159/arrivals?duration=10' -s | jq
|
|||
|
||||
\`\`\`shell
|
||||
# stop/station to POI
|
||||
curl 'https://v5.db.transport.rest/journeys?from=8010159&to.id=991561765&to.latitude=51.483641&to.longitude=11.980841' -s | jq
|
||||
curl 'https://v6.db.transport.rest/journeys?from=8010159&to.id=991561765&to.latitude=51.483641&to.longitude=11.980841' -s | jq
|
||||
# without buses, with ticket info
|
||||
curl 'https://v5.db.transport.rest/journeys?from=…&to=…&bus=false&tickets=true' -s | jq
|
||||
curl 'https://v6.db.transport.rest/journeys?from=…&to=…&bus=false&tickets=true' -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
'/journeys/:ref': `\
|
||||
|
@ -388,11 +386,11 @@ curl 'https://v5.db.transport.rest/journeys?from=…&to=…&bus=false&tickets=tr
|
|||
|
||||
\`\`\`shell
|
||||
# get the refreshToken of a journey
|
||||
journey=$(curl 'https://v5.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
journey=$(curl 'https://v6.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
refresh_token=$(echo $journey | jq -r '.refreshToken')
|
||||
|
||||
# refresh the journey
|
||||
curl "https://v5.db.transport.rest/journeys/$(echo $refresh_token | url-encode)" -s | jq
|
||||
curl "https://v6.db.transport.rest/journeys/$(echo $refresh_token | url-encode)" -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
'/trips/:id': `\
|
||||
|
@ -400,12 +398,12 @@ curl "https://v5.db.transport.rest/journeys/$(echo $refresh_token | url-encode)"
|
|||
|
||||
\`\`\`shell
|
||||
# get the trip ID of a journey leg
|
||||
journey=$(curl 'https://v5.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
journey=$(curl 'https://v6.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
journey_leg=$(echo $journey | jq -r '.legs[0]')
|
||||
trip_id=$(echo $journey_leg | jq -r '.tripId')
|
||||
|
||||
# fetch the trip
|
||||
curl "https://v5.db.transport.rest/trips/$(echo $trip_id | url-encode)" -s | jq
|
||||
curl "https://v6.db.transport.rest/trips/$(echo $trip_id | url-encode)" -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
'/radar': `\
|
||||
|
@ -413,45 +411,47 @@ curl "https://v5.db.transport.rest/trips/$(echo $trip_id | url-encode)" -s | jq
|
|||
|
||||
\`\`\`shell
|
||||
bbox='north=53.555&west=9.989&south=53.55&east=10.001'
|
||||
curl "https://v5.db.transport.rest/radar?$bbox&results=10" -s | jq
|
||||
curl "https://v6.db.transport.rest/radar?$bbox&results=10" -s | jq
|
||||
\`\`\`
|
||||
`,
|
||||
}
|
||||
|
||||
const {
|
||||
listOfRoutes,
|
||||
routes,
|
||||
tail,
|
||||
} = generateApiDocs(api.routes)
|
||||
const generateMarkdownApiDocs = async function* () {
|
||||
const {
|
||||
listOfRoutes,
|
||||
routes,
|
||||
tail,
|
||||
} = generateApiDocs(api.routes)
|
||||
|
||||
const sortedRoutes = Object.entries(routes)
|
||||
.sort(([routeA], [routeB]) => {
|
||||
const iA = order.indexOf(routeA)
|
||||
const iB = order.indexOf(routeB)
|
||||
if (iA >= 0 && iB >= 0) return iA - iB
|
||||
if (iA < 0 && iB >= 0) return 1
|
||||
if (iB < 0 && iA >= 0) return -1
|
||||
return 0
|
||||
})
|
||||
const sortedRoutes = Object.entries(routes)
|
||||
.sort(([routeA], [routeB]) => {
|
||||
const iA = order.indexOf(routeA)
|
||||
const iB = order.indexOf(routeB)
|
||||
if (iA >= 0 && iB >= 0) return iA - iB
|
||||
if (iA < 0 && iB >= 0) return 1
|
||||
if (iB < 0 && iA >= 0) return -1
|
||||
return 0
|
||||
})
|
||||
|
||||
const write = process.stdout.write.bind(process.stdout)
|
||||
yield HEAD
|
||||
yield `\n\n`
|
||||
|
||||
write(HEAD)
|
||||
write(`\n\n`)
|
||||
yield listOfRoutes
|
||||
yield `\n\n`
|
||||
|
||||
write(listOfRoutes)
|
||||
write(`\n\n`)
|
||||
|
||||
for (const [route, params] of sortedRoutes) {
|
||||
write(`## \`GET ${route}\`\n\n`)
|
||||
write(descriptions[route] || '')
|
||||
write(`
|
||||
### Query Parameters
|
||||
`)
|
||||
write(params)
|
||||
if (examples[route]) {
|
||||
write('\n' + examples[route])
|
||||
for (const [route, params] of sortedRoutes) {
|
||||
yield `## \`GET ${route}\`\n\n`
|
||||
yield descriptions[route] || ''
|
||||
yield `\n### Query Parameters\n`
|
||||
yield params
|
||||
if (examples[route]) {
|
||||
yield '\n' + examples[route]
|
||||
}
|
||||
yield `\n\n`
|
||||
}
|
||||
write(`\n\n`)
|
||||
yield tail
|
||||
}
|
||||
write(tail)
|
||||
|
||||
export {
|
||||
generateMarkdownApiDocs,
|
||||
}
|
86
build/index.js
Executable file
86
build/index.js
Executable file
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// todo: use import assertions once they're supported by Node.js & ESLint
|
||||
// https://github.com/tc39/proposal-import-assertions
|
||||
import {createRequire} from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
import {dirname, join} from 'node:path'
|
||||
import {pipeline} from 'node:stream/promises'
|
||||
import {createReadStream, createWriteStream} from 'node:fs'
|
||||
import {copyFile} from 'node:fs/promises'
|
||||
import _technicalDocsCli from '@derhuerst/technical-docs-cli'
|
||||
import {config} from '../api.js'
|
||||
const {
|
||||
createMarkdownRenderer,
|
||||
determineSyntaxStylesheetPath,
|
||||
} = _technicalDocsCli
|
||||
import {generateMarkdownApiDocs} from './api-docs.js'
|
||||
|
||||
const pkg = require('../package.json')
|
||||
|
||||
const BASE_URL = new URL('..', import.meta.url).href
|
||||
const API_DOCS_DEST = 'docs/api.md'
|
||||
const DOCS_TO_RENDER = [
|
||||
['docs/readme.md', 'docs/index.html'],
|
||||
['docs/getting-started.md', 'docs/getting-started.html'],
|
||||
['docs/api.md', 'docs/api.html'],
|
||||
]
|
||||
const SYNTAX_STYLESHEET_URL = '/syntax.css'
|
||||
const SYNTAX_STYLESHEET_SRC = determineSyntaxStylesheetPath('github')
|
||||
const SYNTAX_STYLESHEET_DEST = 'docs/syntax.css'
|
||||
|
||||
{
|
||||
console.info('writing Markdown API docs to ' + API_DOCS_DEST)
|
||||
|
||||
const destPath = new URL(API_DOCS_DEST, BASE_URL).pathname
|
||||
await pipeline(
|
||||
generateMarkdownApiDocs(),
|
||||
createWriteStream(destPath),
|
||||
)
|
||||
}
|
||||
|
||||
const markdownRenderingCfg = {
|
||||
syntaxStylesheetUrl: SYNTAX_STYLESHEET_URL,
|
||||
additionalHeadChildren: (h) => {
|
||||
if (!pkg.goatCounterUrl) return []
|
||||
return [
|
||||
// https://72afc0822cce0642af90.goatcounter.com/help/skip-dev#skip-loading-staging-beta-sites-312
|
||||
h('script', `
|
||||
if (window.location.host !== ${JSON.stringify(config.hostname)}) {
|
||||
window.goatcounter = {no_onload: true}
|
||||
}
|
||||
`),
|
||||
// https://72afc0822cce0642af90.goatcounter.com/help/start
|
||||
h('script', {
|
||||
src: '//gc.zgo.at/count.js',
|
||||
async: true,
|
||||
'data-goatcounter': pkg.goatCounterUrl,
|
||||
}),
|
||||
]
|
||||
},
|
||||
}
|
||||
for (const [src, dest] of DOCS_TO_RENDER) {
|
||||
console.info(`rendering Markdown file ${src} to HTML file ${dest}`)
|
||||
|
||||
const srcPath = new URL(src, BASE_URL).pathname
|
||||
const destPath = new URL(dest, BASE_URL).pathname
|
||||
// unfortunately, we can't use stream.pipeline right now
|
||||
// see https://github.com/unifiedjs/unified-stream/issues/1
|
||||
await new Promise((resolve, reject) => {
|
||||
createReadStream(srcPath)
|
||||
.once('error', reject)
|
||||
.pipe(createMarkdownRenderer(markdownRenderingCfg))
|
||||
.once('error', reject)
|
||||
.pipe(createWriteStream(destPath))
|
||||
.once('error', reject)
|
||||
.once('finish', resolve)
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
const srcPath = SYNTAX_STYLESHEET_SRC
|
||||
const destPath = new URL(SYNTAX_STYLESHEET_DEST, BASE_URL).pathname
|
||||
console.info(`copying syntax stylesheet from ${srcPath} to ${destPath}`)
|
||||
await copyFile(srcPath, destPath)
|
||||
}
|
620
docs/api.md
620
docs/api.md
|
@ -1,620 +0,0 @@
|
|||
# `v5.db.transport.rest` API documentation
|
||||
|
||||
[`v5.db.transport.rest`](https://v5.db.transport.rest/) is a [REST API](https://restfulapi.net). Data is being returned as [JSON](https://www.json.org/).
|
||||
|
||||
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 request/minute (burst 200 requests/minute) set up.
|
||||
|
||||
[OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv5.db.transport.rest%2F.well-known%2Fservice-desc%0A)
|
||||
|
||||
*Note:* The examples snippets in this documentation uses the `url-encode` CLI tool of the [`url-decode-encode-cli` package](https://www.npmjs.com/package/url-decode-encode-cli) for [URL-encoding](https://de.wikipedia.org/wiki/URL-Encoding).
|
||||
|
||||
|
||||
## Routes
|
||||
|
||||
*Note:* These routes only wrap [`hafas-client@5` methods](https://github.com/public-transport/hafas-client/blob/5/docs/readme.md), check their docs for more details.
|
||||
|
||||
|
||||
- [`GET /stops/nearby`](#get-stopsnearby)
|
||||
- [`GET /stops/reachable-from`](#get-stopsreachable-from)
|
||||
- [`GET /stops/:id`](#get-stopsid)
|
||||
- [`GET /stops/:id/departures`](#get-stopsiddepartures)
|
||||
- [`GET /stops/:id/arrivals`](#get-stopsidarrivals)
|
||||
- [`GET /journeys`](#get-journeys)
|
||||
- [`GET /trips/:id`](#get-tripsid)
|
||||
- [`GET /locations`](#get-locations)
|
||||
- [`GET /radar`](#get-radar)
|
||||
- [`GET /journeys/:ref`](#get-journeysref)
|
||||
- [`GET /stations/:id`](#get-stationsid)
|
||||
- [`GET /stations`](#get-stations)
|
||||
- [date/time parameters](#datetime-parameters)
|
||||
|
||||
|
||||
## `GET /locations`
|
||||
|
||||
Uses [`hafasClient.locations()`](https://github.com/public-transport/hafas-client/blob/5/docs/locations.md) to **find stops/stations, POIs and addresses matching `query`**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`query` | **Required.** | string | –
|
||||
`fuzzy` | Find more than exact matches? | boolean | `true`
|
||||
`results` | How many stations shall be shown? | integer | `10`
|
||||
`stops` | Show stops/stations? | boolean | `true`
|
||||
`addresses` | Show points of interest? | boolean | `true`
|
||||
`poi` | Show addresses? | boolean | `true`
|
||||
`linesOfStops` | Parse & return lines of each stop/station? | boolean | `false`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
curl 'https://v5.db.transport.rest/locations?query=halle&results=1' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "8010159",
|
||||
"name": "Halle (Saale) Hbf",
|
||||
"location": {
|
||||
"type": "location",
|
||||
"id": "8010159",
|
||||
"latitude": 51.477079,
|
||||
"longitude": 11.98699
|
||||
},
|
||||
"products": {
|
||||
"nationalExpress": true,
|
||||
"national": true,
|
||||
// …
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## `GET /stops/nearby`
|
||||
|
||||
Uses [`hafasClient.nearby()`](https://github.com/public-transport/hafas-client/blob/5/docs/nearby.md) to **find stops/stations close to the given geolocation**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`latitude` | **Required.** | number | –
|
||||
`longitude` | **Required.** | number | –
|
||||
`results` | maximum number of results | integer | `8`
|
||||
`distance` | maximum walking distance in meters | integer | –
|
||||
`stops` | Return stops/stations? | boolean | `true`
|
||||
`poi` | Return points of interest? | boolean | `false`
|
||||
`linesOfStops` | Parse & expose lines at each stop/station? | boolean | `false`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
curl 'https://v5.db.transport.rest/stops/nearby?latitude=53.5711&longitude=10.0015' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "694800",
|
||||
"name": "Böttgerstraße, Hamburg",
|
||||
"location": {
|
||||
"type": "location",
|
||||
"id": "694800",
|
||||
"latitude": 53.568356,
|
||||
"longitude": 9.995528
|
||||
},
|
||||
"products": { /* … */ },
|
||||
"distance": 498,
|
||||
},
|
||||
// …
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "694802",
|
||||
"name": "Bahnhof Dammtor, Hamburg",
|
||||
"location": {
|
||||
"type": "location",
|
||||
"id": "694802",
|
||||
"latitude": 53.561048,
|
||||
"longitude": 9.990315
|
||||
},
|
||||
"products": { /* … */ },
|
||||
"distance": 1340,
|
||||
},
|
||||
// …
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## `GET /stops/reachable-from`
|
||||
|
||||
Uses [`hafasClient.reachableFrom()`](https://github.com/public-transport/hafas-client/blob/5/docs/reachable-from.md) to **find stops/stations reachable within a certain time from an address**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`latitude` | **Required.** | number | –
|
||||
`longitude` | **Required.** | number | –
|
||||
`address` | **Required.** | string | –
|
||||
`when` | Date & time to compute the reachability for. See [date/time parameters](#datetime-parameters). | date+time | *now*
|
||||
`maxTransfers` | Maximum number of transfers. | integer | `5`
|
||||
`maxDuration` | Maximum travel duration, in minutes. | integer | *infinite*
|
||||
`language` | Language of the results. | string | `en`
|
||||
`nationalExpress` | Include InterCityExpress (ICE)? | boolean | `true`
|
||||
`national` | Include InterCity & EuroCity (IC/EC)? | boolean | `true`
|
||||
`regionalExp` | Include RegionalExpress & InterRegio (RE/IR)? | boolean | `true`
|
||||
`regional` | Include Regio (RB)? | boolean | `true`
|
||||
`suburban` | Include S-Bahn (S)? | boolean | `true`
|
||||
`bus` | Include Bus (B)? | boolean | `true`
|
||||
`ferry` | Include Ferry (F)? | boolean | `true`
|
||||
`subway` | Include U-Bahn (U)? | boolean | `true`
|
||||
`tram` | Include Tram (T)? | boolean | `true`
|
||||
`taxi` | Include Group Taxi (Taxi)? | boolean | `true`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
curl 'https://v5.db.transport.rest/stops/reachable-from?latitude=53.553766&longitude=9.977514&address=Hamburg,+Holstenwall+9' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"duration": 1,
|
||||
"stations": [
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "694815",
|
||||
"name": "Handwerkskammer, Hamburg",
|
||||
"location": { /* … */ },
|
||||
"products": { /* … */ },
|
||||
},
|
||||
]
|
||||
},
|
||||
// …
|
||||
{
|
||||
"duration": 5,
|
||||
"stations": [
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "694807",
|
||||
"name": "Feldstraße (U), Hamburg",
|
||||
"location": { /* … */ },
|
||||
"products": { /* … */ },
|
||||
// …
|
||||
},
|
||||
// …
|
||||
]
|
||||
},
|
||||
// …
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## `GET /stops/:id`
|
||||
|
||||
Uses [`hafasClient.stop()`](https://github.com/public-transport/hafas-client/blob/5/docs/stop.md) to **find a stop/station by ID**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`linesOfStops` | Parse & expose lines at each stop/station? | boolean | `false`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
curl 'https://v5.db.transport.rest/stops/8010159' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
{
|
||||
"type": "stop",
|
||||
"id": "8010159",
|
||||
"ids": {
|
||||
"dhid": "de:15002:8010159",
|
||||
"MDV": "8010159",
|
||||
"NASA": "8010159"
|
||||
},
|
||||
"name": "Halle (Saale) Hbf",
|
||||
"location": {
|
||||
"type": "location",
|
||||
"id": "8010159",
|
||||
"latitude": 51.477079,
|
||||
"longitude": 11.98699
|
||||
},
|
||||
"products": { /* … */ },
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## `GET /stops/:id/departures`
|
||||
|
||||
Uses [`hafasClient.departures()`](https://github.com/public-transport/hafas-client/blob/5/docs/departures.md) to **get departures at a stop/station**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`when` | Date & time to get departures for. See [date/time parameters](#datetime-parameters). | date+time | *now*
|
||||
`direction` | Filter departures by direction. | string |
|
||||
`duration` | Show departures for how many minutes? | integer | `10`
|
||||
`results` | Max. number of departures. | integer | *whatever HAFAS wants
|
||||
`linesOfStops` | Parse & return lines of each stop/station? | boolean | `false`
|
||||
`remarks` | Parse & return hints & warnings? | boolean | `true`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`nationalExpress` | Include InterCityExpress (ICE)? | boolean | `true`
|
||||
`national` | Include InterCity & EuroCity (IC/EC)? | boolean | `true`
|
||||
`regionalExp` | Include RegionalExpress & InterRegio (RE/IR)? | boolean | `true`
|
||||
`regional` | Include Regio (RB)? | boolean | `true`
|
||||
`suburban` | Include S-Bahn (S)? | boolean | `true`
|
||||
`bus` | Include Bus (B)? | boolean | `true`
|
||||
`ferry` | Include Ferry (F)? | boolean | `true`
|
||||
`subway` | Include U-Bahn (U)? | boolean | `true`
|
||||
`tram` | Include Tram (T)? | boolean | `true`
|
||||
`taxi` | Include Group Taxi (Taxi)? | boolean | `true`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
# at Halle (Saale) Hbf, in direction Berlin Südkreuz
|
||||
curl 'https://v5.db.transport.rest/stops/8010159/departures?direction=8011113&duration=120' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"tripId": "1|317591|0|80|1052020",
|
||||
"direction": "Berlin Hbf (tief)",
|
||||
"line": {
|
||||
"type": "line",
|
||||
"id": "ice-702",
|
||||
"name": "ICE 702",
|
||||
"mode": "train",
|
||||
"product": "nationalExpress",
|
||||
// …
|
||||
},
|
||||
|
||||
"when": "2020-05-01T21:06:00+02:00",
|
||||
"plannedWhen": "2020-05-01T21:06:00+02:00",
|
||||
"delay": 0,
|
||||
"platform": "8",
|
||||
"plannedPlatform": "8",
|
||||
|
||||
"stop": {
|
||||
"type": "stop",
|
||||
"id": "8010159",
|
||||
"name": "Halle (Saale) Hbf",
|
||||
"location": { /* … */ },
|
||||
"products": { /* … */ },
|
||||
},
|
||||
|
||||
"remarks": [],
|
||||
// …
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## `GET /stops/:id/arrivals`
|
||||
|
||||
Works like [`/stops/:id/departures`](#get-stopsiddepartures), except that it uses [`hafasClient.arrivals()`](https://github.com/public-transport/hafas-client/blob/5/docs/arrivals.md) to **arrivals at a stop/station**.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`when` | Date & time to get departures for. See [date/time parameters](#datetime-parameters). | date+time | *now*
|
||||
`direction` | Filter departures by direction. | string |
|
||||
`duration` | Show departures for how many minutes? | integer | `10`
|
||||
`results` | Max. number of departures. | integer | *whatever HAFAS wants*
|
||||
`linesOfStops` | Parse & return lines of each stop/station? | boolean | `false`
|
||||
`remarks` | Parse & return hints & warnings? | boolean | `true`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`nationalExpress` | Include InterCityExpress (ICE)? | boolean | `true`
|
||||
`national` | Include InterCity & EuroCity (IC/EC)? | boolean | `true`
|
||||
`regionalExp` | Include RegionalExpress & InterRegio (RE/IR)? | boolean | `true`
|
||||
`regional` | Include Regio (RB)? | boolean | `true`
|
||||
`suburban` | Include S-Bahn (S)? | boolean | `true`
|
||||
`bus` | Include Bus (B)? | boolean | `true`
|
||||
`ferry` | Include Ferry (F)? | boolean | `true`
|
||||
`subway` | Include U-Bahn (U)? | boolean | `true`
|
||||
`tram` | Include Tram (T)? | boolean | `true`
|
||||
`taxi` | Include Group Taxi (Taxi)? | boolean | `true`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
# at Halle (Saale) Hbf, 10 minutes
|
||||
curl 'https://v5.db.transport.rest/stops/8010159/arrivals?duration=10' -s | jq
|
||||
```
|
||||
|
||||
|
||||
## `GET /journeys`
|
||||
|
||||
Uses [`hafasClient.journeys()`](https://github.com/public-transport/hafas-client/blob/5/docs/journeys.md) to **find journeys from A (`from`) to B (`to`)**.
|
||||
|
||||
`from` (A), `to` (B), and the optional `via` must each have one of these formats:
|
||||
|
||||
- as stop/station ID (e.g. `from=8010159` for *Halle (Saale) Hbf*)
|
||||
- as a POI (e.g. `from.id=991561765&from.latitude=51.48364&from.longitude=11.98084` for *Halle+(Saale),+Stadtpark+Halle+(Grünanlagen)*)
|
||||
- as an address (e.g. `from.latitude=51.25639&from.longitude=7.46685&from.address=Hansestadt+Breckerfeld,+Hansering+3` for *Hansestadt Breckerfeld, Hansering 3*)
|
||||
|
||||
### Pagination
|
||||
|
||||
Given a response, you can also fetch more journeys matching the same criteria. Instead of `from*`, `to*` & `departure`/`arrival`, pass `earlierRef` from the first response as `earlierThan` to get journeys "before", or `laterRef` as `laterThan` to get journeys "after".
|
||||
|
||||
Check the [`hafasClient.journeys()` docs](https://github.com/public-transport/hafas-client/blob/5/docs/journeys.md) for more details.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`departure` | Compute journeys departing at this date/time. Mutually exclusive with `arrival`. See [date/time parameters](#datetime-parameters). | date+time | *now*
|
||||
`arrival` | Compute journeys arriving at this date/time. Mutually exclusive with `departure`. See [date/time parameters](#datetime-parameters). | date+time | *now*
|
||||
`earlierThan` | Compute journeys "before" an `ealierRef`. | string |
|
||||
`laterThan` | Compute journeys "after" an `laterRef`. | string |
|
||||
`results` | Max. number of journeys. | integer | `3`
|
||||
`stopovers` | Fetch & parse stopovers on the way? | boolean | `false`
|
||||
`transfers` | Maximum number of transfers. | integer | *let HAFAS decide*
|
||||
`transferTime` | Minimum time in minutes for a single transfer. | integer | `0`
|
||||
`accessibility` | `partial` or `complete`. | string | *not accessible*
|
||||
`bike` | Compute only bike-friendly journeys? | boolean | `false`
|
||||
`startWithWalking` | Consider walking to nearby stations at the beginning of a journey? | boolean | `true`
|
||||
`walkingSpeed` | `slow`, `normal` or `fast`. | string | `normal`
|
||||
`tickets` | Return information about available tickets? | boolean | `false`
|
||||
`polylines` | Fetch & parse a shape for each journey leg? | boolean | `false`
|
||||
`remarks` | Parse & return hints & warnings? | boolean | `true`
|
||||
`scheduledDays` | Parse & return dates each journey is valid on? | boolean | `false`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`loyaltyCard` | Type of loyalty card in use. | string | *none*
|
||||
`firstClass` | Search for first-class options? | boolean | `false`
|
||||
`nationalExpress` | Include InterCityExpress (ICE)? | boolean | `true`
|
||||
`national` | Include InterCity & EuroCity (IC/EC)? | boolean | `true`
|
||||
`regionalExp` | Include RegionalExpress & InterRegio (RE/IR)? | boolean | `true`
|
||||
`regional` | Include Regio (RB)? | boolean | `true`
|
||||
`suburban` | Include S-Bahn (S)? | boolean | `true`
|
||||
`bus` | Include Bus (B)? | boolean | `true`
|
||||
`ferry` | Include Ferry (F)? | boolean | `true`
|
||||
`subway` | Include U-Bahn (U)? | boolean | `true`
|
||||
`tram` | Include Tram (T)? | boolean | `true`
|
||||
`taxi` | Include Group Taxi (Taxi)? | boolean | `true`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Examples
|
||||
|
||||
```shell
|
||||
# stop/station to POI
|
||||
curl 'https://v5.db.transport.rest/journeys?from=8010159&to.id=991561765&to.latitude=51.483641&to.longitude=11.980841' -s | jq
|
||||
# without buses, with ticket info
|
||||
curl 'https://v5.db.transport.rest/journeys?from=…&to=…&bus=false&tickets=true' -s | jq
|
||||
```
|
||||
|
||||
|
||||
## `GET /journeys/:ref`
|
||||
|
||||
Uses [`hafasClient.refreshJourney()`](https://github.com/public-transport/hafas-client/blob/5/docs/refresh-journey.md) to **"refresh" a journey, using its `refreshToken`**.
|
||||
|
||||
The journey will be the same (equal `from`, `to`, `via`, date/time & vehicles used), but you can get up-to-date realtime data, like delays & cancellations.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`stopovers` | Fetch & parse stopovers on the way? | boolean | `false`
|
||||
`tickets` | Return information about available tickets? | boolean | `false`
|
||||
`polylines` | Fetch & parse a shape for each journey leg? | boolean | `false`
|
||||
`remarks` | Parse & return hints & warnings? | boolean | `true`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
# get the refreshToken of a journey
|
||||
journey=$(curl 'https://v5.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
refresh_token=$(echo $journey | jq -r '.refreshToken')
|
||||
|
||||
# refresh the journey
|
||||
curl "https://v5.db.transport.rest/journeys/$(echo $refresh_token | url-encode)" -s | jq
|
||||
```
|
||||
|
||||
|
||||
## `GET /trips/:id`
|
||||
|
||||
Uses [`hafasClient.trip()`](https://github.com/public-transport/hafas-client/blob/5/docs/trip.md) to **fetch a trip by ID**.
|
||||
|
||||
A trip is a specific vehicle, stopping at a series of stops at specific points in time. Departures, arrivals & journey legs reference trips by their ID.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`lineName` | **Required.** Line name of the part's mode of transport, e.g. `RE7`. | string | –
|
||||
`stopovers` | Fetch & parse stopovers on the way? | boolean | `true`
|
||||
`remarks` | Parse & return hints & warnings? | boolean | `true`
|
||||
`polyline` | Fetch & parse the geographic shape of the trip? | boolean | `false`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
# get the trip ID of a journey leg
|
||||
journey=$(curl 'https://v5.db.transport.rest/journeys?from=…&to=…&results=1' -s | jq '.journeys[0]')
|
||||
journey_leg=$(echo $journey | jq -r '.legs[0]')
|
||||
trip_id=$(echo $journey_leg | jq -r '.tripId')
|
||||
|
||||
# fetch the trip
|
||||
curl "https://v5.db.transport.rest/trips/$(echo $trip_id | url-encode)" -s | jq
|
||||
```
|
||||
|
||||
|
||||
## `GET /stations`
|
||||
|
||||
If the `query` parameter is used, it will use [`db-stations-autocomplete@2`](https://github.com/derhuerst/db-stations-autocomplete/tree/2.2.0) to autocomplete *Deutsche Bahn*-operated stops/stations. Otherwise, it will filter the stops/stations in [`db-stations@3`](https://github.com/derhuerst/db-stations/tree/3.0.1).
|
||||
|
||||
Instead of receiving a JSON response, you can request [newline-delimited JSON](http://ndjson.org) by sending `Accept: application/x-ndjson`.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`query` | Find stations by name using [`db-stations-autocomplete@2`](https://github.com/derhuerst/db-stations-autocomplete/tree/2.2.0). | string | –
|
||||
`limit` | *If `query` is used:* Return at most `n` stations. | number | `3`
|
||||
`fuzzy` | *If `query` is used:* Find stations despite typos. | boolean | `false`
|
||||
`completion` | *If `query` is used:* Autocomplete stations. | boolean | `true`
|
||||
|
||||
### Examples
|
||||
|
||||
```shell
|
||||
# autocomplete using db-stations-autocomplete
|
||||
curl 'https://v5.db.transport.rest/stations?query=dammt' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
{
|
||||
"8002548": {
|
||||
"id": "8002548",
|
||||
"relevance": 0.8572361756428573,
|
||||
"score": 9.175313823998414,
|
||||
"weight": 1212,
|
||||
"type": "station",
|
||||
"ril100": "ADF",
|
||||
"name": "Hamburg Dammtor",
|
||||
"location": {
|
||||
"type": "location",
|
||||
"latitude": 53.560751,
|
||||
"longitude": 9.989566
|
||||
},
|
||||
"operator": {
|
||||
"type": "operator",
|
||||
"id": "hamburger-verkehrsverbund-gmbh",
|
||||
"name": "BWVI"
|
||||
},
|
||||
"address": {
|
||||
"city": "Hamburg",
|
||||
"zipcode": "20354",
|
||||
"street": "Dag-Hammarskjöld-Platz 15"
|
||||
},
|
||||
// …
|
||||
},
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
# filter db-stations by `hasParking` property
|
||||
curl 'https://v5.db.transport.rest/stations?hasParking=true' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
{
|
||||
"8000001": {
|
||||
"type": "station",
|
||||
"id": "8000001",
|
||||
"ril100": "KA",
|
||||
"name": "Aachen Hbf",
|
||||
"weight": 653.75,
|
||||
"location": { /* … */ },
|
||||
"operator": { /* … */ },
|
||||
"address": { /* … */ },
|
||||
// …
|
||||
},
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
```shell
|
||||
# filter db-stations by `hasDBLounge` property, get newline-delimited JSON
|
||||
curl 'https://v5.db.transport.rest/stations?hasDBLounge=true' -H 'accept: application/x-ndjson' -s | jq
|
||||
```
|
||||
|
||||
|
||||
## `GET /stations/:id`
|
||||
|
||||
Returns a stop/station from [`db-stations`](https://npmjs.com/package/db-stations).
|
||||
|
||||
### Query Parameters
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
# lookup Halle (Saale) Hbf
|
||||
curl 'https://v5.db.transport.rest/stations/8010159' -s | jq
|
||||
curl 'https://v5.db.transport.rest/stations/LH' -s | jq # RIL100/DS100
|
||||
curl 'https://v5.db.transport.rest/stations/LHG' -s | jq # RIL100/DS100
|
||||
```
|
||||
|
||||
```js
|
||||
{
|
||||
"type": "station",
|
||||
"id": "8010159",
|
||||
"additionalIds": ["8098159"],
|
||||
"ril100": "LH",
|
||||
"nr": 2498,
|
||||
"name": "Halle (Saale) Hbf",
|
||||
"weight": 815.6,
|
||||
"location": { /* … */ },
|
||||
"operator": { /* … */ },
|
||||
"address": { /* … */ },
|
||||
"ril100Identifiers": [
|
||||
{
|
||||
"rilIdentifier": "LH",
|
||||
// …
|
||||
},
|
||||
// …
|
||||
],
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## `GET /radar`
|
||||
|
||||
Uses [`hafasClient.radar()`](https://github.com/public-transport/hafas-client/blob/5/docs/radar.md) to **find all vehicles currently in an area**, as well as their movements.
|
||||
|
||||
### Query Parameters
|
||||
|
||||
parameter | description | type | default value
|
||||
----------|-------------|------|--------------
|
||||
`north` | **Required.** Northern latitude. | number | –
|
||||
`west` | **Required.** Western longitude. | number | –
|
||||
`south` | **Required.** Southern latitude. | number | –
|
||||
`east` | **Required.** Eastern longitude. | number | –
|
||||
`results` | Max. number of vehicles. | integer | `256`
|
||||
`duration` | Compute frames for the next `n` seconds. | integer | `30`
|
||||
`frames` | Number of frames to compute. | integer | `3`
|
||||
`polylines` | Fetch & parse a geographic shape for the movement of each vehicle? | boolean | `true`
|
||||
`language` | Language of the results. | string | `en`
|
||||
`pretty` | Pretty-print JSON responses? | boolean | `true`
|
||||
|
||||
### Example
|
||||
|
||||
```shell
|
||||
bbox='north=53.555&west=9.989&south=53.55&east=10.001'
|
||||
curl "https://v5.db.transport.rest/radar?$bbox&results=10" -s | jq
|
||||
```
|
||||
|
||||
|
||||
## Date/Time Parameters
|
||||
|
||||
Possible formats:
|
||||
|
||||
- anything that [`parse-human-relative-time`](https://npmjs.com/package/parse-human-relative-time) can parse (e.g. `tomorrow 2pm`)
|
||||
- [ISO 8601 date/time string](https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations) (e.g. `2020-04-26T22:43+02:00`)
|
||||
- [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) (e.g. `1587933780`)
|
|
@ -1,4 +1,4 @@
|
|||
# Getting Started with `v5.db.transport.rest`
|
||||
# Getting Started with `v6.db.transport.rest`
|
||||
|
||||
Let's walk through the **requests that are necessary to implement a typical basic transit app**.
|
||||
|
||||
|
@ -11,7 +11,7 @@ The following code snippets use [`curl`](https://curl.haxx.se) (a versatile comm
|
|||
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
|
||||
curl 'https://v6.db.transport.rest/locations?poi=false&addresses=false&query=südkreuz' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
|
@ -78,7 +78,7 @@ curl 'https://v5.db.transport.rest/locations?poi=false&addresses=false&query=sü
|
|||
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
|
||||
curl 'https://v6.db.transport.rest/stops/8011113/departures?results=5' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
|
@ -160,7 +160,7 @@ We call a connection from A to B – at a specific date & time, made up of secti
|
|||
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
|
||||
curl 'https://v6.db.transport.rest/journeys?from=8011113&to=8010159&departure=tomorrow+2pm&results=2' -s | jq
|
||||
```
|
||||
|
||||
```js
|
||||
|
@ -316,4 +316,4 @@ Note that `departure` includes the `departureDelay`, and `arrival` includes the
|
|||
|
||||
### 4. more features
|
||||
|
||||
These are the basics. Check the full [API docs](api.md) for all features or use the [OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv5.db.transport.rest%2F.well-known%2Fservice-desc%0A) or explore the API!
|
||||
These are the basics. Check the full [API docs](api.md) for all features or use the [OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv6.db.transport.rest%2F.well-known%2Fservice-desc%0A) or explore the API!
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
# `v5.db.transport.rest` documentation
|
||||
# `v6.db.transport.rest` documentation
|
||||
|
||||
[`v5.db.transport.rest`](https://v5.db.transport.rest/) is a [REST API](https://restfulapi.net) for the public transportation system in Germany.
|
||||
[`v6.db.transport.rest`](https://v6.db.transport.rest/) is a [REST API](https://restfulapi.net) for the public transportation system in Germany.
|
||||
|
||||
[](https://stats.uptimerobot.com/57wNLs39M/784879516)
|
||||
[](https://stats.uptimerobot.com/57wNLs39M/793274556)
|
||||
|
||||
Because it wraps [an API](https://github.com/public-transport/hafas-client/blob/master/readme.md#background) of [Deutsche Bahn](https://de.wikipedia.org/wiki/Deutsche_Bahn), it **includes most of the long-distance and regional traffic, as well as some international trains and local buses**. Essentially, it returns whatever data the [*DB Navigator* app](https://www.bahn.de/p/view/service/mobile/db-navigator.shtml) shows, **including realtime delays and disruptions**.
|
||||
Because it wraps [an API](https://github.com/public-transport/hafas-client/blob/6/readme.md#background) of [Deutsche Bahn](https://de.wikipedia.org/wiki/Deutsche_Bahn), it **includes most of the long-distance and regional traffic, as well as some international trains and local buses**. Essentially, it returns whatever data the [*DB Navigator* app](https://www.bahn.de/p/view/service/mobile/db-navigator.shtml) shows*, **including realtime delays and disruptions**.
|
||||
|
||||
*When comparing results from this API to what the DB Navigator app shows there might be occasional differences due to them utilizing different, not 100% identical backends.*
|
||||
|
||||
- [Getting Started](getting-started.md)
|
||||
- [API documentation](api.md)
|
||||
- [OpenAPI playground](https://petstore.swagger.io/?url=https%3A%2F%2Fv5.db.transport.rest%2F.well-known%2Fservice-desc%0A)
|
||||
- [API documentation](api.md) (run `npm run build` to generate)
|
||||
- [OpenAPI playground with API documentation](https://petstore.swagger.io/?url=https%3A%2F%2Fv6.db.transport.rest%2F.well-known%2Fservice-desc%0A)
|
||||
|
||||
## Why use this API?
|
||||
|
||||
### Realtime Data
|
||||
|
||||
This API returns realtime data whenever its upstream, the [API for DB's mobile app](https://github.com/public-transport/hafas-client/blob/33d7d30acf235c54887c6459a15fe581982c6a19/p/db/readme.md), provides it.
|
||||
This API returns realtime data whenever it is upstream. The [API for DB's mobile app](https://github.com/public-transport/hafas-client/blob/6/p/db/readme.md) provides it.
|
||||
|
||||
*Note: Different endpoints might remove realtime data like delays and cancellations at different times, i.e. after a journey's arrival.*
|
||||
|
||||
### No API Key
|
||||
|
||||
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 requests/minute (burst 150 requests/minute) set up.
|
||||
You can just use the API without authentication. There's a [rate limit](https://apisyouwonthate.com/blog/what-is-api-rate-limiting-all-about) of 100 requests/minute set up.
|
||||
|
||||
### CORS
|
||||
|
||||
|
|
4
index.js
4
index.js
|
@ -1,6 +1,4 @@
|
|||
'use strict'
|
||||
|
||||
const {api, config} = require('./api')
|
||||
import {api, config} from './api.js'
|
||||
|
||||
api.listen(config.port, (err) => {
|
||||
const {logger} = api.locals
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
'use strict'
|
||||
import {createRequire} from 'node:module'
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
const {statSync} = require('fs')
|
||||
const {full: readRawStations} = require('db-stations')
|
||||
import {statSync} from 'node:fs'
|
||||
import {readFullStations} from 'db-stations'
|
||||
|
||||
// We don't have access to the publish date+time of the npm package,
|
||||
// so we use the ctime of db-stations/full.ndjson as an approximation.
|
||||
// Also require() doesn't make much sense in ES Modules.
|
||||
// todo: this is brittle, find a better way, e.g. a build script
|
||||
const timeModified = statSync(require.resolve('db-stations/full.ndjson')).ctime
|
||||
|
||||
const pStations = new Promise((resolve, reject) => {
|
||||
let raw = readRawStations()
|
||||
let raw = readFullStations()
|
||||
raw.once('error', reject)
|
||||
|
||||
let data = Object.create(null)
|
||||
|
@ -37,4 +39,6 @@ pStations.catch((err) => {
|
|||
process.exit(1) // todo: is this appropriate?
|
||||
})
|
||||
|
||||
module.exports = pStations
|
||||
export {
|
||||
pStations,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const {data: cards} = require('hafas-client/p/db/loyalty-cards')
|
||||
import {data as cards} from 'hafas-client/p/db/loyalty-cards.js'
|
||||
|
||||
const typesByName = new Map([
|
||||
// https://github.com/public-transport/hafas-client/blob/68ecd7c5e976dd2f51c5c64a81600e7e181a8996/p/db/loyalty-cards.js#L6-L11
|
||||
|
@ -29,8 +29,8 @@ const loyaltyCardParser = {
|
|||
parse: parseLoyaltyCard,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loyaltyCards: cards,
|
||||
export {
|
||||
cards as loyaltyCards,
|
||||
parseLoyaltyCard,
|
||||
loyaltyCardParser,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2022, Jannis R
|
||||
Copyright (c) 2024, Jannis R
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
|
|
6216
package-lock.json
generated
Normal file
6216
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
45
package.json
45
package.json
|
@ -2,10 +2,21 @@
|
|||
"private": true,
|
||||
"name": "db-rest",
|
||||
"description": "A clean REST API wrapping around the Deutsche Bahn API.",
|
||||
"version": "5.0.2",
|
||||
"version": "6.0.5",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"db-rest": "./index.js"
|
||||
},
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"docs",
|
||||
"lib",
|
||||
"routes",
|
||||
"api.js",
|
||||
"index.js"
|
||||
],
|
||||
"author": "Jannis R <mail@jannisr.de>",
|
||||
"homepage": "https://github.com/derhuerst/db-rest/tree/5",
|
||||
"homepage": "https://github.com/derhuerst/db-rest/tree/6",
|
||||
"repository": "derhuerst/db-rest",
|
||||
"bugs": "https://github.com/derhuerst/db-rest/issues",
|
||||
"license": "ISC",
|
||||
|
@ -19,36 +30,36 @@
|
|||
"db"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"cached-hafas-client": "^4.0.4",
|
||||
"cached-hafas-client": "^5.0.1",
|
||||
"cli-native": "^1.0.0",
|
||||
"db-hafas": "^5.0.2",
|
||||
"db-stations": "^3.0.0",
|
||||
"db-stations-autocomplete": "^2.2.0",
|
||||
"db-hafas": "^6.0.0",
|
||||
"db-stations": "^5.0.0",
|
||||
"db-stations-autocomplete": "^4.0.0",
|
||||
"etag": "^1.8.1",
|
||||
"hafas-client-health-check": "^2.1.1",
|
||||
"hafas-rest-api": "^3.8.0",
|
||||
"hafas-rest-api": "^5.1.0",
|
||||
"ioredis": "^5.0.6",
|
||||
"serve-buffer": "^2.0.0",
|
||||
"serve-buffer": "^3.0.3",
|
||||
"serve-static": "^1.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@derhuerst/technical-docs-cli": "^1.1.0",
|
||||
"axios": "^0.26.1",
|
||||
"@derhuerst/technical-docs-cli": "^1.5.0",
|
||||
"axios": "^1.6.1",
|
||||
"eslint": "^8.12.0",
|
||||
"get-port": "^5.1.1",
|
||||
"get-port": "^6.1.2",
|
||||
"ndjson": "^2.0.0",
|
||||
"pino-pretty": "^4.0.0",
|
||||
"tap-min": "^2.0.0",
|
||||
"pino-pretty": "^9.1.1",
|
||||
"tap-min": "^3.0.0",
|
||||
"tape": "^5.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"docs": "node api-docs.js >docs/api.md && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/readme.md >docs/index.html && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/getting-started.md >docs/getting-started.html && build-technical-doc --syntax-stylesheet-url /syntax.css <docs/api.md >docs/api.html && build-technical-doc --syntax-stylesheet github >docs/syntax.css",
|
||||
"build": "npm run docs",
|
||||
"build": "REDIS_URL='' ./build/index.js",
|
||||
"start": "node index.js",
|
||||
"lint": "eslint .",
|
||||
"test": "node test/index.js | tap-min"
|
||||
}
|
||||
},
|
||||
"goatCounterUrl": "https://37462fdee48390778258.goatcounter.com/count"
|
||||
}
|
||||
|
|
20
readme.md
20
readme.md
|
@ -1,12 +1,12 @@
|
|||
# db-rest
|
||||
|
||||
**A clean REST API wrapping around the [Deutsche Bahn HAFAS API](https://github.com/public-transport/db-hafas#db-hafas).** It is deployed at [`v5.db.transport.rest`](https://v5.db.transport.rest/).
|
||||
**A clean REST API wrapping around the [Deutsche Bahn HAFAS API](https://github.com/public-transport/db-hafas#db-hafas).** It is deployed at [`v6.db.transport.rest`](https://v6.db.transport.rest/).
|
||||
|
||||
[**API Documentation**](docs/readme.md)
|
||||
|
||||

|
||||
|
||||
[](https://stats.uptimerobot.com/57wNLs39M/784879516)
|
||||
[](https://stats.uptimerobot.com/57wNLs39M/793274556)
|
||||
[](https://david-dm.org/derhuerst/db-rest)
|
||||

|
||||
[](https://github.com/sponsors/derhuerst)
|
||||
|
@ -15,14 +15,16 @@
|
|||
|
||||
## installing & running
|
||||
|
||||
`db-rest` expects a [Redis](https://redis.io/) server running on `127.0.0.1:6379` (default port), but you can set the `REDIS_URL` environment variable to change this.
|
||||
### access to Redis
|
||||
|
||||
It is recommended that you let `bvg-rest` cache HAFAS responses within a [Redis](https://redis.io/) cache. To use this feature, set `$REDIS_URL` (e.g. to `redis://localhost:6379/1` when running Redis locally).
|
||||
|
||||
### via Docker
|
||||
|
||||
A Docker image [is available as `derhuerst/db-rest:5`](https://hub.docker.com/r/derhuerst/db-rest:5).
|
||||
A Docker image [is available as `docker.io/derhuerst/db-rest:6`](https://hub.docker.com/r/docker.io/derhuerst/db-rest:6).
|
||||
|
||||
```shell
|
||||
docker run -d -p 3000:3000 derhuerst/db-rest:5
|
||||
docker run -d -p 3000:3000 docker.io/derhuerst/db-rest:6
|
||||
```
|
||||
|
||||
*Note:* The Docker image does not contain the Redis server.
|
||||
|
@ -32,8 +34,11 @@ docker run -d -p 3000:3000 derhuerst/db-rest:5
|
|||
```shell
|
||||
git clone https://github.com/derhuerst/db-rest.git
|
||||
cd db-rest
|
||||
git checkout 5
|
||||
npm install --production
|
||||
git checkout 6
|
||||
npm install
|
||||
|
||||
export HOSTNAME='my-vbb-rest-api.example.org'
|
||||
npm run build
|
||||
|
||||
redis-server &
|
||||
npm start
|
||||
|
@ -44,6 +49,7 @@ To keep the API running permanently, use tools like [`forever`](https://github.c
|
|||
|
||||
## Related Projects
|
||||
|
||||
- [`DB-Adapter-v6`](https://github.com/olech2412/DB-Adapter-v6) – A Java API client for `db-rest`.
|
||||
- [`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.
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
'use strict'
|
||||
|
||||
const pStations = require('../lib/db-stations')
|
||||
import {pStations} from '../lib/db-stations.js'
|
||||
|
||||
const err404 = (msg) => {
|
||||
const err = new Error(msg)
|
||||
|
@ -58,4 +56,6 @@ Returns a stop/station from [\`db-stations@3\`](https://github.com/derhuerst/db-
|
|||
},
|
||||
}
|
||||
|
||||
module.exports = stationRoute
|
||||
export {
|
||||
stationRoute as route,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
'use strict'
|
||||
|
||||
const computeEtag = require('etag')
|
||||
const serveBuffer = require('serve-buffer')
|
||||
const autocomplete = require('db-stations-autocomplete')
|
||||
const parse = require('cli-native').to
|
||||
const createFilter = require('db-stations/create-filter')
|
||||
let pAllStations = require('../lib/db-stations')
|
||||
import computeEtag from 'etag'
|
||||
import serveBuffer from 'serve-buffer'
|
||||
import {autocomplete} from 'db-stations-autocomplete'
|
||||
import _cliNative from 'cli-native'
|
||||
const {to: parse} = _cliNative
|
||||
import {createFilter} from 'db-stations/create-filter.js'
|
||||
import {pStations} from '../lib/db-stations.js'
|
||||
|
||||
const JSON_MIME = 'application/json'
|
||||
const NDJSON_MIME = 'application/x-ndjson'
|
||||
|
@ -24,7 +23,7 @@ const toNdjsonBuf = (data) => {
|
|||
return Buffer.concat(chunks, bytes)
|
||||
}
|
||||
|
||||
pAllStations = pAllStations.then(({data, timeModified}) => {
|
||||
const pAllStations = pStations.then(({data, timeModified}) => {
|
||||
const asJson = Buffer.from(JSON.stringify(data), 'utf8')
|
||||
const asNdjson = toNdjsonBuf(data)
|
||||
return {
|
||||
|
@ -202,4 +201,6 @@ stationsRoute.queryParameters = {
|
|||
},
|
||||
}
|
||||
|
||||
module.exports = stationsRoute
|
||||
export {
|
||||
stationsRoute as route,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
'use strict'
|
||||
|
||||
const tape = require('tape')
|
||||
const {parse: ndjsonParser} = require('ndjson')
|
||||
const {loyaltyCards} = require('../lib/loyalty-cards')
|
||||
const {fetchWithTestApi} = require('./util')
|
||||
const pAllStations = require('../lib/db-stations')
|
||||
import tape from 'tape'
|
||||
import _ndjson from 'ndjson'
|
||||
const {parse: ndjsonParser} = _ndjson
|
||||
import {loyaltyCards} from '../lib/loyalty-cards.js'
|
||||
import {fetchWithTestApi} from './util.js'
|
||||
import {pStations as pAllStations} from '../lib/db-stations.js'
|
||||
|
||||
const NO_JOURNEYS = {
|
||||
// todo?
|
||||
|
@ -76,11 +75,11 @@ tape.test('/stations works', async (t) => {
|
|||
}
|
||||
})
|
||||
|
||||
tape.test('/stations?query=frankf works', async (t) => {
|
||||
tape.test('/stations?query=frankfurt%20ha works', async (t) => {
|
||||
const FRANKFURT_MAIN_HBF = '8000105'
|
||||
|
||||
{
|
||||
const {headers, data} = await fetchWithTestApi({}, {}, '/stations?query=frankf', {
|
||||
const {headers, data} = await fetchWithTestApi({}, {}, '/stations?query=frankfurt%20ha', {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
},
|
||||
|
@ -93,7 +92,7 @@ tape.test('/stations?query=frankf works', async (t) => {
|
|||
}
|
||||
|
||||
{
|
||||
const {headers, data} = await fetchWithTestApi({}, {}, '/stations?query=frankf', {
|
||||
const {headers, data} = await fetchWithTestApi({}, {}, '/stations?query=frankfurt%20ha', {
|
||||
headers: {
|
||||
'accept': 'application/x-ndjson',
|
||||
},
|
||||
|
@ -106,6 +105,6 @@ tape.test('/stations?query=frankf works', async (t) => {
|
|||
for await (const station of parser) stations.push(station)
|
||||
|
||||
t.ok(stations.find(s => s.id === FRANKFURT_MAIN_HBF))
|
||||
t.ok(Object.keys(stations).length > 0)
|
||||
t.ok(stations.length > 0)
|
||||
}
|
||||
})
|
||||
|
|
19
test/util.js
19
test/util.js
|
@ -1,12 +1,9 @@
|
|||
'use strict'
|
||||
|
||||
const createApi = require('hafas-rest-api')
|
||||
const getPort = require('get-port')
|
||||
const {createServer} = require('http')
|
||||
const {promisify} = require('util')
|
||||
const axios = require('axios')
|
||||
const pkg = require('../package.json')
|
||||
const {config, hafas: unmockedHafas} = require('../api')
|
||||
import {createHafasRestApi} from 'hafas-rest-api'
|
||||
import getPort from 'get-port'
|
||||
import {createServer} from 'node:http'
|
||||
import {promisify} from 'node:util'
|
||||
import axios from 'axios'
|
||||
import {config, hafas as unmockedHafas} from '../api.js'
|
||||
|
||||
// adapted from https://github.com/public-transport/hafas-rest-api/blob/60335eacd8332d7f448da875a7498dd97934e360/test/util.js#L40-L77
|
||||
const createTestApi = async (mocks, cfg) => {
|
||||
|
@ -24,7 +21,7 @@ const createTestApi = async (mocks, cfg) => {
|
|||
...cfg,
|
||||
}
|
||||
|
||||
const api = createApi(mockedHafas, cfg, () => {})
|
||||
const api = await createHafasRestApi(mockedHafas, cfg, () => {})
|
||||
const server = createServer(api)
|
||||
|
||||
const port = await getPort()
|
||||
|
@ -50,7 +47,7 @@ const fetchWithTestApi = async (mocks, cfg, path, opt = {}) => {
|
|||
return res
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
createTestApi,
|
||||
fetchWithTestApi,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue